Added rate limiting and server security

Ton of little visual style tweaks and little up improvements for mobile
This commit is contained in:
Max G 2020-03-26 04:45:23 +00:00
parent 749d2cea94
commit ecbf6a9cde
17 changed files with 283 additions and 148 deletions

View File

@ -6,7 +6,7 @@ mkdir -p $BACKUPDIR
cd $BACKUPDIR cd $BACKUPDIR
NOW=$(date +"%Y-%m-%d_%H-%M") NOW=$(date +"%Y-%m-%d_%H-%M")
ssh mab@avidhabit.com -p 13328 "mysqldump --all-databases --user root -p***REMOVED***" > "backup-$NOW.sql" ssh mab@solidscribe.com -p 13328 "mysqldump --all-databases --user root -p***REMOVED***" > "backup-$NOW.sql"
cp "backup-$NOW.sql" "/mnt/Windows Data/DatabaseBackups/backup-$NOW.sql" cp "backup-$NOW.sql" "/mnt/Windows Data/DatabaseBackups/backup-$NOW.sql"

View File

@ -18,14 +18,14 @@ tar -czf release.tar.gz server node_modules client/dist staticFiles/assets
#send compressed release to remote machine #send compressed release to remote machine
echo -e "\e[32m\nMoving compressed release to production... \n\e[0m" echo -e "\e[32m\nMoving compressed release to production... \n\e[0m"
rsync -e 'ssh -p 13328' -havzC --update release.tar.gz mab@avidhabit.com:/home/mab/pi/ rsync -e 'ssh -p 13328' -havzC --update release.tar.gz mab@solidscribe.com:/home/mab/pi/
# Remove Release from local after its been uploaded # Remove Release from local after its been uploaded
rm release.tar.gz rm release.tar.gz
#uncompress release on server #uncompress release on server
echo -e "\e[32m\nExtracting release on production... \n\e[0m" echo -e "\e[32m\nExtracting release on production... \n\e[0m"
ssh mab@avidhabit.com -p 13328 "cd /home/mab/pi/; rm -r server node_modules client; tar -xzf *.tar.gz --overwrite; rm *.tar.gz; pm2 reload all" ssh mab@solidscribe.com -p 13328 "cd /home/mab/pi/; rm -r server node_modules client; tar -xzf *.tar.gz --overwrite; rm *.tar.gz; pm2 reload all"
#Congratulate how awesome you are #Congratulate how awesome you are
echo -e "\e[32m\nRelease Complete! Nice Work! \n\e[0m" echo -e "\e[32m\nRelease Complete! Nice Work! \n\e[0m"

View File

@ -1,5 +1,5 @@
<template> <template>
<div id="app"> <div id="app" :class="{ 'night-mode':($store.getters.getIsNightMode) }">
<global-site-menu /> <global-site-menu />

View File

@ -35,6 +35,14 @@ div.ui.basic.segment.no-fluf-segment {
margin-top: 0px; margin-top: 0px;
} }
/* Night mode modifiers */
/*Make images sepia in night mode */
.night-mode img {
filter: grayscale(50%) brightness(80%) sepia(80%);
}
/* OVERWRITE DEFAULT SEMANTIC STYLES FOR CUSTOM/NIGHT MODES*/ /* OVERWRITE DEFAULT SEMANTIC STYLES FOR CUSTOM/NIGHT MODES*/
body { body {
color: var(--text_color); color: var(--text_color);
@ -231,22 +239,27 @@ a:hover {
.squire-box a { .squire-box a {
cursor: pointer; cursor: pointer;
} }
/* .note-card-text i, .note-card-text i:not(.icon),
.squire-box i { .squire-box i {
padding: 0.5em 0.99em; padding: 0.5em 0.99em;
border: 1px solid #CCC; border-radius: 1px;
margin: 1px;
border-radius: 9px;
display: inline-block; display: inline-block;
}*/ font-style: normal;
background-color: rgba(113, 113, 113, 0.1);
}
.night-mode .note-card-text i:not(.icon),
.night-mode .squire-box i {
background-color: rgba(255, 255, 255, 0.2);
}
.note-card-text p,
.squire-box p { .squire-box p {
margin-bottom: 0; margin-bottom: 0;
} }
.note-card-text blockquote, .note-card-text blockquote,
.squire-box blockquote { .squire-box blockquote {
margin: 0; margin: 0;
padding: 0.8em; padding: 0 0 0 2.5em;
border-left: 2px solid blue;
} }
.note-card-text img { .note-card-text img {
max-width:100%; max-width:100%;
@ -305,6 +318,10 @@ a:hover {
/* adjust checkboxes for mobile. Make them a little bigger, easier to click */ /* adjust checkboxes for mobile. Make them a little bigger, easier to click */
@media only screen and (max-width: 740px) { @media only screen and (max-width: 740px) {
.ui.button.shrinking {
font-size: 0.85714286rem;
}
.note-card-text ul > li, .note-card-text ul > li,
.squire-box ul > li { .squire-box ul > li {
min-height: 30px; min-height: 30px;
@ -346,6 +363,12 @@ a:hover {
.float-right { .float-right {
float: right; float: right;
} }
.ui.grid.reduced-padding > .column {
padding-left: 2px;
padding-right: 2px;
padding-top: 5px;
padding-bottom: 5px;
}
.textarea-height { .textarea-height {
height: calc(100% - 90px); height: calc(100% - 90px);

View File

@ -18,6 +18,11 @@
left: 0; left: 0;
bottom: 0; bottom: 0;
} }
.menu-logo-display {
width: 25px;
margin: 5px 0 0 34px;
display: inline-block;
}
.menu-item { .menu-item {
color: #fff; color: #fff;
@ -101,33 +106,41 @@
<i class="green bars icon"></i> <i class="green bars icon"></i>
</div> </div>
<router-link class="ui large basic compact icon button" to="/notes" v-on:click.native="emitReloadEvent()"> <router-link v-if="loggedIn" class="ui large basic compact icon button" to="/notes" v-on:click.native="emitReloadEvent()">
<i class="green home icon"></i> <i class="green home icon"></i>
</router-link> </router-link>
<router-link v-if="loggedIn" class="ui basic icon button" exact-active-class="active" to="/attachments">
<i class="open folder outline icon"></i>
</router-link>
</div>
<div class="two wide center aligned bottom aligned column">
<img loading="lazy" src="/api/static/assets/logo.svg" alt="Solid Scribe Logo">
</div>
<div class="seven wide right aligned column">
<div v-on:click="toggleNightMode" class="ui large basic compact icon button"> <div v-on:click="toggleNightMode" class="ui large basic compact icon button">
<i class="green moon outline icon"></i> <i class="green moon outline icon"></i>
</div> </div>
</div>
<div class="six wide center aligned column">
<img v-if="!loggedIn" src="/api/static/assets/favicon.ico" alt="logo" />
<search-input v-if="loggedIn && mobile"></search-input> <search-input v-if="loggedIn && mobile"></search-input>
</div>
<div class="three wide right aligned column">
<!-- mobile create note button --> <!-- mobile create note button -->
<div v-if="loggedIn"> <span v-if="loggedIn">
<div v-if="!disableNewNote" @click="createNote" class="ui large basic compact icon button"> <span v-if="!disableNewNote" @click="createNote" class="ui large basic compact icon button">
<i class="green plus icon"></i> <i class="green plus icon"></i>
</div> </span>
<div v-if="disableNewNote" class="ui large basic compact icon button"> <span v-if="disableNewNote" class="ui large basic compact icon button">
<i class="grey plus icon"></i> <i class="grey plus icon"></i>
</div> </span>
</span>
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="shade" v-if="mobile && !collapsed" v-on:click="collapseMenu"></div> <div class="shade" v-if="mobile && !collapsed" v-on:click="collapseMenu"></div>
@ -136,10 +149,11 @@
<div class="global-menu" v-if="!collapsed" v-on:click="menuClicked"> <div class="global-menu" v-if="!collapsed" v-on:click="menuClicked">
<div class="menu-section"> <div class="menu-section" v-on:click="collapseMenu">
<div class="menu-item menu-button" v-on:click="collapseMenu"> <!-- <div class="menu-item menu-button" > -->
<i class="angle left icon"></i> <i class="white angle left icon"></i>
</div> <img class="menu-logo-display" loading="lazy" src="/api/static/assets/logo.svg" alt="Solid Scribe Logo">
<!-- </div> -->
</div> </div>
<div class="menu-section" v-if="loggedIn"> <div class="menu-section" v-if="loggedIn">

View File

@ -19,23 +19,24 @@
<div class="note-menu"> <div class="note-menu">
<nm-button v-on:click.native="close" icon="close" /> <nm-button tip="Close" bottom-tip="true" v-on:click.native="close" icon="close" />
<nm-button v-on:click.native="toggleList('ol')" icon="list ol" /> <nm-button tip="Numbered List" bottom-tip="true" v-on:click.native="toggleList('ol')" icon="list ol" />
<nm-button v-on:click.native="toggleList('ul')" icon="tasks" /> <nm-button tip="Task List" bottom-tip="true" v-on:click.native="toggleList('ul')" icon="tasks" />
<nm-button v-on:click.native="toggleBold()" icon="bold" /> <nm-button tip="Bold" bottom-tip="true" v-on:click.native="toggleBold()" icon="bold" />
<nm-button v-on:click.native="toggleItalic()" icon="quote left" /> <nm-button tip="Quote" bottom-tip="true" v-on:click.native="toggleItalic()" icon="quote left" />
<nm-button v-on:click.native="modifyFont('1.4em')" icon="text height" /> <nm-button tip="Title" bottom-tip="true" v-on:click.native="modifyFont('1.4em')" icon="text height" />
<nm-button v-on:click.native="undoCustom()" icon="undo" /> <nm-button tip="Indent" bottom-tip="true" v-on:click.native="editor.increaseQuoteLevel()" icon="indent" />
<nm-button v-if="usersOnNote > 1" icon="green user circle" /> <nm-button tip="Outdent" bottom-tip="true" v-on:click.native="editor.decreaseQuoteLevel()" icon="outdent" />
<nm-button tip="Users on Note" bottom-tip="true" v-if="usersOnNote > 1" icon="green user circle" />
<nm-button icon="ellipsis horizontal" v-on:click.native="showNoteOptions = !showNoteOptions" />
</div> </div>
<!-- Squire box grows --> <!-- Squire box grows -->
@ -92,25 +93,52 @@
<!-- bottom stats --> <!-- bottom stats -->
<div class="ui basic segment"> <div class="ui basic segment">
<div class="ui grid"> <div class="ui grid reduced-padding">
<div class="sixteen wide column">
<div class="ui basic button"v-if="!isEncrypted" v-on:click="passwordEnterVisible = true"> <div class="four wide column">
<!-- Tags -->
<button class="ui compact basic fluid button shrinking" v-on:click="showTagSlideMenu = !showTagSlideMenu; modified = true">
<i class="tags icon"></i> Tags
</button>
</div>
<div class="four wide column">
<!-- Archive Button -->
<button class="ui compact basic fluid button shrinking" v-on:click="onToggleArchived">
<span v-if="archived == 1"><i class="green archive icon"></i> Archived</span>
<span v-if="archived != 1"><i class="archive icon"></i> Archive</span>
</button>
</div>
<div class="four wide column"><!-- Pin button -->
<button class="ui compact basic fluid button shrinking" v-on:click="onTogglePinned">
<span v-if="pinned == 1"><i class="green pin icon"></i> Pinned</span>
<span v-if="pinned != 1"><i class="pin icon"></i> Pin</span>
</button></div>
<div class="four wide column">
<!-- files button -->
<button class="ui compact basic fluid button shrinking" v-on:click="openEditAttachment">
<i class="folder icon"></i> Files
</button>
</div>
<!-- <div class="sixteen wide column"></div> -->
<div class="eight wide column">
<button class="ui basic compact button shrinking" v-if="!isEncrypted" v-on:click="passwordEnterVisible = true">
<i class="shield alternate icon"></i> <i class="shield alternate icon"></i>
Password Protect Password Protect
</div> </button>
<div class="ui icon basic button" v-if="isEncrypted && isDecrypted" v-on:click="disableEncryption"> <button class="ui icon basic compact button shrinking" v-if="isEncrypted && isDecrypted" v-on:click="disableEncryption">
<i class="unlock icon"></i> <i class="unlock icon"></i>
Remove Password Remove Password
</button>
</div> </div>
<div class="seven wide right aligned middle aligned column">
<div class="ui basic compact button"> <span :data-tooltip="`Created: ${$helpers.timeAgo(created)}`">
Status: {{ statusText }} Edited: {{ $helpers.timeAgo(updated) }}
</div> </span>
<div class="ui basic compact button" :data-tooltip="`Created: ${$helpers.timeAgo(created)}`">
Last Change: {{ $helpers.timeAgo(updated) }}
</div>
</div> </div>
<div class="one wide column"></div>
</div> </div>
</div> </div>
@ -127,30 +155,12 @@
<div class="note-menu shrink-icons-on-mobile"> <div class="note-menu shrink-icons-on-mobile">
<!-- Pin Button -->
<nm-button
v-on:click.native="onToggleArchived"
:icon="(archived == 1)?'green archive':'archive'"
:text="(archived == 1)?'Archived':'Archive'"
tip="Show in archive"
:showText="true"
></nm-button>
<!-- archive button -->
<nm-button
v-on:click.native="onTogglePinned"
:icon="(pinned == 1)?'green pin':'pin'"
:text="(pinned == 1)?'Pinned':'Pin'"
tip="Pin to top of list"
:showText="true"
></nm-button>
<!-- colors button --> <!-- colors button -->
<nm-button <nm-button
v-on:click.native="showColorPicker" v-on:click.native="showColorPicker"
icon="paint brush" icon="paint brush"
text="Colors" text="Color"
tip="Colors" tip="Note Color"
></nm-button> ></nm-button>
<!-- add images panel --> <!-- add images panel -->
@ -161,27 +171,18 @@
tip="Images" tip="Images"
></nm-button> ></nm-button>
<!-- Tags -->
<nm-button
v-on:click.native="showTagSlideMenu = !showTagSlideMenu; modified = true"
icon="tags"
text="Tags"
tip="Tags"
></nm-button>
<!-- file upload button --> <!-- file upload button -->
<file-upload-button <file-upload-button
class="nm-button" class="nm-button"
:noteId="noteid" /> :noteId="noteid" />
<!-- files button --> <nm-button v-on:click.native="undoCustom()" icon="undo" tip="Undo" text="Undo" />
<nm-button <nm-button
v-on:click.native="openEditAttachment" icon="ellipsis horizontal"
icon="folder" text="Options"
text="Files" tip="More Options"
tip="Files on Note" v-on:click.native="showNoteOptions = !showNoteOptions" />
:showText="true"
></nm-button>
</div> </div>
</div> </div>
@ -238,12 +239,6 @@
Uncheck all Checked items Uncheck all Checked items
</div> </div>
</div> </div>
<div class="eight wide column">
<div class="ui labeled icon fluid basic button" v-on:click="undoCustom">
<i class="undo icon"></i>
Undo last change
</div>
</div>
<div class="eight wide column"> <div class="eight wide column">
<div class="ui labeled icon fluid basic button" v-on:click="calculateMath" data-tooltip="Calculates algebra before '='"> <div class="ui labeled icon fluid basic button" v-on:click="calculateMath" data-tooltip="Calculates algebra before '='">
<i class="calculator icon"></i> <i class="calculator icon"></i>
@ -251,6 +246,7 @@
</div> </div>
</div> </div>
<div class="sixteen wide column" v-if="rawTextId > 0"> <div class="sixteen wide column" v-if="rawTextId > 0">
<h2>Share Note</h2>
<share-note-component <share-note-component
:note-id="noteid" :note-id="noteid"
:raw-text-id="rawTextId" :raw-text-id="rawTextId"
@ -274,7 +270,7 @@
<h2><i class="green lock alternate icon"></i>Password protect this Note</h2> <h2><i class="green lock alternate icon"></i>Password protect this Note</h2>
<p>Password protection will prevent anyone from reading the text of this note, unless they enter the correct password.</p> <p>Password protection will prevent anyone from reading the text of this note, unless they enter the correct password.</p>
<p><b>Only the note text is protected. Title, tags, and files are not encrypted and remain visible without a password.</b></p> <p><b>Only the note text is protected. Title, tags, and files are not encrypted and remain visible without a password.</b></p>
<p>The password you select will only be used for this note. You can use the same password on multiple notes. The note will be encrypted using the password entered. A longer password is will be more secure.</p> <p>The password you select will only be used for this note. You can use the same password on multiple notes. The note will be encrypted using the password entered. A longer password will be more secure.</p>
<h4><i class="red icon exclamation triangle"></i> Warning. There is no way to recover a lost password.</h4> <h4><i class="red icon exclamation triangle"></i> Warning. There is no way to recover a lost password.</h4>
</div> </div>

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="nm-button" :class="moreClass" :data-tooltip="tip" data-inverted> <div class="nm-button" :class="moreClass" :data-tooltip="tip" data-inverted :data-position=" bottomTip?'bottom center':'top center'">
<!-- Display Icon and text --> <!-- Display Icon and text -->
<i v-if="icon" :class="`${icon} icon`"></i> <i v-if="icon" :class="`${icon} icon`"></i>
<span v-if="(text && mobile) || (text && showText)">{{text}}</span> <span v-if="(text && mobile) || (text && showText)">{{text}}</span>
@ -21,12 +21,11 @@
export default { export default {
name: 'NoteMenuButtonComponent', name: 'NoteMenuButtonComponent',
props: [ 'icon', 'text', 'tooltip', 'moreClass', 'showText', 'tip'], props: [ 'icon', 'text', 'tooltip', 'moreClass', 'showText', 'tip', 'bottomTip'],
data () { data () {
return { return {
files: [], files: [],
mobile: false, mobile: false,
showTooltip: false,
} }
}, },
beforeMount(){ beforeMount(){

View File

@ -1,10 +1,46 @@
<style type="text/css" scoped>
.fixed-search {
position: fixed;
top: 50%;
left: 0;
right: 0;
padding: 10px;
}
</style>
<template> <template>
<div class="ui form"> <span>
<div class="ui form" v-if="!$store.getters.getIsUserOnMobile">
<!-- normal search menu -->
<div class="ui left icon fluid input"> <div class="ui left icon fluid input">
<input v-model="searchTerm" @keyup="searchKeyUp" @:keyup.enter="search" placeholder="Search Notes and Files" /> <input v-model="searchTerm" @keyup="searchKeyUp" @keyup.enter="search" placeholder="Search Notes and Files" ref="searchInput"/>
<i class="search icon"></i> <i class="search icon"></i>
</div> </div>
</div> </div>
<span class="ui basic icon button" v-on:click="openFloatingSearch">
<i class="green search icon"></i>
</span>
<div class="fixed-search" v-if="showFixedSearch">
<div class="ui raised segment">
<h2 class="ui center aligned header">Search!</h2>
<div class="ui form">
<div class="ui left icon fluid input">
<input
ref="fixedSearch"
v-model="searchTerm"
@keyup.enter="search"
v-on:blur="showFixedSearch = false"
placeholder="Press Enter to Search" />
<i class="search icon"></i>
</div>
</div>
</div>
</div>
</span>
</template> </template>
<script> <script>
@ -16,6 +52,7 @@
searchTerm: '', searchTerm: '',
searchTimeout: null, searchTimeout: null,
searchDebounceDuration: 300, searchDebounceDuration: 300,
showFixedSearch: false,
} }
}, },
beforeCreate: function(){ beforeCreate: function(){
@ -29,13 +66,27 @@
}, },
methods: { methods: {
openFloatingSearch(){
this.showFixedSearch = !this.showFixedSearch
if(this.showFixedSearch){
this.$nextTick( () => {
this.searchTerm = ''
this.$refs.fixedSearch.focus()
})
}
},
searchKeyUp(){ searchKeyUp(){
//This event is not triggered on mobile
clearTimeout(this.searchTimeout) clearTimeout(this.searchTimeout)
this.searchTimeout = setTimeout(() => { this.searchTimeout = setTimeout(() => {
this.search() this.search()
}, this.searchDebounceDuration) }, this.searchDebounceDuration)
}, },
search(){ search(){
if(this.$store.getters.getIsUserOnMobile){
this.$refs.fixedSearch.blur()
}
this.$bus.$emit('update_search_term', this.searchTerm) this.$bus.$emit('update_search_term', this.searchTerm)
}, },
} }

View File

@ -1,8 +1,8 @@
<template> <template>
<div class="ui basic segment no-fluf-segment" ref="content"> <div class="ui basic segment no-fluf-segment" ref="content">
<div class="ui grid"> <div class="ui stacking grid">
<div class="ui twelve wide column"> <div class="sixteen wide column">
<h2 class="ui header"> <h2 class="ui header">
<i class="folder open outline icon"></i> <i class="folder open outline icon"></i>
<div class="content"> <div class="content">
@ -14,7 +14,7 @@
<!-- subnav --> <!-- subnav -->
<router-link <router-link
exact-active-class="green" exact-active-class="green"
class="ui basic button" class="ui basic button shrinking"
to="/attachments"> to="/attachments">
<i class="open folder outline icon"></i> <i class="open folder outline icon"></i>
All All
@ -22,7 +22,7 @@
<router-link <router-link
v-if="$store.getters.totals && $store.getters.totals['linkFiles']" v-if="$store.getters.totals && $store.getters.totals['linkFiles']"
exact-active-class="green" exact-active-class="green"
class="ui basic button" class="ui basic button shrinking"
to="/attachments/type/links"> to="/attachments/type/links">
<i class="linkify icon"></i> <i class="linkify icon"></i>
Links Links
@ -30,16 +30,13 @@
<router-link <router-link
v-if="$store.getters.totals && $store.getters.totals['otherFiles']" v-if="$store.getters.totals && $store.getters.totals['otherFiles']"
exact-active-class="green" exact-active-class="green"
class="ui basic button" class="ui basic button shrinking"
to="/attachments/type/files"> to="/attachments/type/files">
<i class="copy icon"></i> <i class="copy icon"></i>
Other Files Other Files
</router-link> </router-link>
</div> </div>
<div class="four wide bottom aligned column">
<i v-if="loading" class="green sync alternate loading icon"></i>
</div>
<div class="sixteen wide column" v-if="searchParams.noteId"> <div class="sixteen wide column" v-if="searchParams.noteId">
<router-link class="ui green button" to="/attachments"> <router-link class="ui green button" to="/attachments">
@ -50,6 +47,8 @@
<i class="file outline icon"></i> <i class="file outline icon"></i>
Open Note Open Note
</div> </div>
<i v-if="loading" class="green sync alternate loading icon"></i>
</div> </div>
<div class="sixteen wide column" v-if="searchParams['noteId'] && attachments.length == 0"> <div class="sixteen wide column" v-if="searchParams['noteId'] && attachments.length == 0">

View File

@ -11,12 +11,16 @@
-moz-animation: fadeorama 16s ease infinite; -moz-animation: fadeorama 16s ease infinite;
animation: fadeorama 16s ease infinite; animation: fadeorama 16s ease infinite;
} }
.logo-display {
width: 50%;
}
.lightly-padded { .lightly-padded {
margin-top: 10px; margin-top: 10px;
} }
.massive-text { .massive-text {
color: white; color: white;
font-size: 4rem; font-size: 4rem;
text-align: center;
} }
.blinking { .blinking {
animation:blinkingText 1.5s linear infinite; animation:blinkingText 1.5s linear infinite;
@ -63,6 +67,9 @@
} }
/*safari fix - prevents page from being below the menu */ /*safari fix - prevents page from being below the menu */
.green-text {
color: #3710a4;
}
.dont-pad-me { .dont-pad-me {
margin-right: 0 !important; margin-right: 0 !important;
margin-left: 0 !important; margin-left: 0 !important;
@ -101,13 +108,18 @@
<!-- desktop column - large screen only --> <!-- desktop column - large screen only -->
<div class="seven wide middle aligned left aligned column"> <div class="seven wide middle aligned left aligned column">
<h2 class="massive-text">Take Notes, <br>Like Never Before</h2>
<h3 class="subtext"> <h2 class="massive-text">
Using an online note application <i class="i cursor icon blinking"></i> <img class="logo-display" loading="lazy" src="/api/static/assets/logo.svg" alt="Solid Scribe Logo">
</h3>
<p>Assuming you have never used a note application previously in your life.</p>
<br> <br>
<i class="huge inverted chevron circle down icon"></i> Solid Scribe
</h2>
<h3 class="subtext">
Take Notes Like Never Before<i class="i cursor icon blinking"></i>
</h3>
<p class="green-text">Assuming you have never used a note application previously in your life.</p>
</div> </div>
<div class="eight wide middle aligned left aligned column"> <div class="eight wide middle aligned left aligned column">
@ -269,7 +281,6 @@
<p> <p>
If you see anything broken or want to see a feature implemented, I'm open to suggestions. <i class="thumbs up icon"></i> If you see anything broken or want to see a feature implemented, I'm open to suggestions. <i class="thumbs up icon"></i>
</p> </p>
<p>Hero Slide Photo Credit - <a target="_blank" href="https://unsplash.com/@tkaslik14">https://unsplash.com/@tkaslik14</a></p>
<p>Generic Marketing Images - <a target="_blank" href="https://undraw.co/">https://unDraw.co/</a></p> <p>Generic Marketing Images - <a target="_blank" href="https://undraw.co/">https://unDraw.co/</a></p>
</div> </div>
<div class="four wide column"> <div class="four wide column">
@ -291,15 +302,14 @@ export default {
realInformation: false, realInformation: false,
} }
}, },
beforeMount(){ beforeCreate(){
//Force HTTPS on prod, always. Dev doesn't have certs //Force HTTPS on prod, always. Dev doesn't have certs
const isDev = process.env['NODE_ENV'] == 'development' const isDev = process.env['NODE_ENV'] == 'development'
if (!isDev && location.protocol != 'https:'){ if (!isDev && location.protocol != 'https:'){
window.location.replace('https://www.avidhabit.com') window.location.replace('https://www.solidscribe.com')
} }
},
beforeMount(){
//Don't change hero banner on mobile //Don't change hero banner on mobile
if(!this.$store.getters.getIsUserOnMobile){ if(!this.$store.getters.getIsUserOnMobile){

View File

@ -14,7 +14,7 @@
<div class="ten wide column" :class="{ 'sixteen wide column':$store.getters.getIsUserOnMobile }"> <div class="ten wide column" :class="{ 'sixteen wide column':$store.getters.getIsUserOnMobile }">
<div class="ui basic button" <div class="ui basic button shrinking"
v-on:click="updateFastFilters(3)" v-on:click="updateFastFilters(3)"
v-if="$store.getters.totals && ($store.getters.totals['sharedToNotes'] > 0 || $store.getters.totals['sharedFromNotes'] > 0)" v-if="$store.getters.totals && ($store.getters.totals['sharedToNotes'] > 0 || $store.getters.totals['sharedFromNotes'] > 0)"
style="position: relative;"> style="position: relative;">
@ -24,12 +24,12 @@
</span> </span>
</div> </div>
<div class="ui basic button" v-on:click="updateFastFilters(2)" v-if="$store.getters.totals && $store.getters.totals['archivedNotes'] > 0"> <div class="ui basic button shrinking" v-on:click="updateFastFilters(2)" v-if="$store.getters.totals && $store.getters.totals['archivedNotes'] > 0">
<i class="green archive icon"></i>Archived <i class="green archive icon"></i>Archived
<!-- <span>{{ $store.getters.totals['archivedNotes'] }}</span> --> <!-- <span>{{ $store.getters.totals['archivedNotes'] }}</span> -->
</div> </div>
<div class="ui basic button" v-on:click="updateFastFilters(4)" v-if="$store.getters.totals && $store.getters.totals['encryptedNotes'] > 0"> <div class="ui basic button shrinking" v-on:click="updateFastFilters(4)" v-if="$store.getters.totals && $store.getters.totals['encryptedNotes'] > 0">
<i class="green lock alternate icon"></i>Locked <i class="green lock alternate icon"></i>Locked
<!-- <span>{{ $store.getters.totals['encryptedNotes'] }}</span> --> <!-- <span>{{ $store.getters.totals['encryptedNotes'] }}</span> -->
</div> </div>

View File

@ -12,8 +12,11 @@
"body-parser": "^1.18.3", "body-parser": "^1.18.3",
"cheerio": "^1.0.0-rc.3", "cheerio": "^1.0.0-rc.3",
"express": "^4.16.4", "express": "^4.16.4",
"express-rate-limit": "^5.1.1",
"gm": "^1.23.1", "gm": "^1.23.1",
"helmet": "^3.21.3",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"module-alias": "^2.2.2",
"multer": "^1.4.2", "multer": "^1.4.2",
"mysql2": "^1.6.5", "mysql2": "^1.6.5",
"node-tesseract-ocr": "^1.0.0", "node-tesseract-ocr": "^1.0.0",

View File

@ -26,6 +26,17 @@ ProcessText.stripBlankHtmlLines = (string) => {
return string.replace(/\<p\>\<br\>\<\/p\>/g,'') return string.replace(/\<p\>\<br\>\<\/p\>/g,'')
} }
//Remove Double Empty HTML lines from a string
ProcessText.stripDoubleBlankLines = (string) => {
if(string == undefined || string == null || string.length == 0){
return ''
}
//Blank lines look like this -> <p><br></p>
return string.replace(/\<p\>\<br\>\<\/p\>\<p\>\<br\>\<\/p\>/g,'')
}
ProcessText.getUrlsFromString = (string) => { ProcessText.getUrlsFromString = (string) => {
const urlPattern = /(?:(?:https?|ftp|file):\/\/|www\.|ftp\.)(?:\([-A-Z0-9+&@#/%=~_|$?!:,.]*\)|[-A-Z0-9+&@#/%=~_|$?!:,.])*(?:\([-A-Z0-9+&@#/%=~_|$?!:,.]*\)|[A-Z0-9+&@#/%=~_|$])/igm const urlPattern = /(?:(?:https?|ftp|file):\/\/|www\.|ftp\.)(?:\([-A-Z0-9+&@#/%=~_|$?!:,.]*\)|[-A-Z0-9+&@#/%=~_|$?!:,.])*(?:\([-A-Z0-9+&@#/%=~_|$?!:,.]*\)|[A-Z0-9+&@#/%=~_|$])/igm
return string.match(urlPattern) return string.match(urlPattern)
@ -41,11 +52,16 @@ ProcessText.getUrlsFromString = (string) => {
+ If note starts as a list, skip the title + If note starts as a list, skip the title
*/ */
ProcessText.deduceNoteTitle = (inString) => { ProcessText.deduceNoteTitle = (inTitle, inString) => {
let title = '' //Title of note let title = inTitle //Title of note
let sub = '' //sub text below note let sub = '' //sub text below note
//Always return a title as a String
if(title == null){
title = ''
}
if(!inString || inString == null || inString.length == 0){ if(!inString || inString == null || inString.length == 0){
return {title, sub} return {title, sub}
} }
@ -55,16 +71,17 @@ ProcessText.deduceNoteTitle = (inString) => {
const tagFreeLength = ProcessText.removeHtml(inString).length const tagFreeLength = ProcessText.removeHtml(inString).length
if(tagFreeLength < 100){ //
sub = ProcessText.stripBlankHtmlLines(inString) // Simplified attempt!
return {title, sub} // Remove tags, push caret if greater than 200 chars...thats it
// Still needs, links to open in a new window.
sub = ProcessText.stripDoubleBlankLines(inString)
if(tagFreeLength > 200){
sub += '... <i class="green caret down icon"></i>'
} }
//Primare Case - Short notes
if(tagFreeLength < 300){
sub = ProcessText.stripBlankHtmlLines(inString)
return {title, sub} return {title, sub}
}
//Emergency ending tag if truncated. This will help regex find all the lines //Emergency ending tag if truncated. This will help regex find all the lines
inString += '</end>' inString += '</end>'
@ -87,6 +104,7 @@ ProcessText.deduceNoteTitle = (inString) => {
let charLimit = 400 let charLimit = 400
let listStart = false let listStart = false
let noTitleJustList = false let noTitleJustList = false
let appendCaret = false
for(let i=0; i < totalLines; i++){ for(let i=0; i < totalLines; i++){
@ -167,8 +185,8 @@ ProcessText.deduceNoteTitle = (inString) => {
if(cleanCutString.length == 0){ if(cleanCutString.length == 0){
cleanCutString = cutString cleanCutString = cutString
} }
appendCaret = true
finalLines.push(cleanCutString + '... <i class="green caret down icon"></i>')
break; break;
} }
@ -176,9 +194,13 @@ ProcessText.deduceNoteTitle = (inString) => {
} }
if(tagFreeLength.length >= 300 || appendCaret){
finalLines.push('... <i class="green caret down icon"></i>')
}
//Pull out title if its not an empty string //Pull out title if its not an empty string
if(ProcessText.removeHtml(finalLines[0]).trim().replace('&nbsp','').length > 0 && !noTitleJustList){ if(!noTitleJustList && title == ''){
// title = finalLines.shift() title = ProcessText.removeHtml( finalLines.shift() ).replace('&nbsp','')
} }
sub = finalLines.join('') sub = finalLines.join('')

View File

@ -3,10 +3,29 @@ require('module-alias/register')
let Auth = require('@helpers/Auth') let Auth = require('@helpers/Auth')
const helmet = require('helmet')
const express = require('express') const express = require('express')
const app = express() const app = express()
app.use( helmet() )
const port = 3000 const port = 3000
//
// Request Rate Limiter
//
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 10 * 60 * 1000, // minutes
max: 1000 // limit each IP to 100 requests per windowMs
});
// apply to all requests
app.use(limiter);
var http = require('http').createServer(app); var http = require('http').createServer(app);
var io = require('socket.io')(http, { var io = require('socket.io')(http, {
path:'/socket' path:'/socket'

View File

@ -512,7 +512,7 @@ Note.solrQuery = (userId, searchQuery, searchTags) => {
} else { } else {
//Number of characters before and after search word //Number of characters before and after search word
const front = 5 const front = 20
const tail = 150 const tail = 150
db.promise() db.promise()
@ -584,7 +584,7 @@ Note.search = (userId, searchQuery, searchTags, fastFilters) => {
let searchParams = [userId] let searchParams = [userId]
let noteSearchQuery = ` let noteSearchQuery = `
SELECT note.id, SELECT note.id,
SUBSTRING(note_raw_text.text, 1, 1500) as text, SUBSTRING(note_raw_text.text, 1, 500) as text,
note_raw_text.title as title, note_raw_text.title as title,
note_raw_text.updated as updated, note_raw_text.updated as updated,
opened, opened,
@ -722,15 +722,10 @@ Note.search = (userId, searchQuery, searchTags, fastFilters) => {
if(note.encrypted == 1){ note.text = '' } if(note.encrypted == 1){ note.text = '' }
//Deduce note title //Deduce note title
const textData = ProcessText.deduceNoteTitle(note.text) const textData = ProcessText.deduceNoteTitle(note.title, note.text)
// console.log(textData) // console.log(textData)
// console.log(textData) note.title = textData.title
if(note.title == null){
note.title = ''
}
note.subtext = textData.sub note.subtext = textData.sub
note.titleLength = textData.titleLength note.titleLength = textData.titleLength
note.subtextLength = textData.subtextLength note.subtextLength = textData.subtextLength

View File

@ -5,6 +5,10 @@ let Tag = module.exports = {}
Tag.userTags = (userId, searchQuery, searchTags, fastFilters) => { Tag.userTags = (userId, searchQuery, searchTags, fastFilters) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if(searchQuery && searchQuery.length > 0){
return resolve([])
}
let query = ` let query = `
SELECT SELECT
tag.id, tag.id,
@ -12,7 +16,7 @@ Tag.userTags = (userId, searchQuery, searchTags, fastFilters) => {
COUNT(note_tag.note_id) as usages COUNT(note_tag.note_id) as usages
FROM tag FROM tag
JOIN note_tag ON tag.id = note_tag.tag_id JOIN note_tag ON tag.id = note_tag.tag_id
JOIN note On note.id = note_tag.note_id JOIN note ON note.id = note_tag.note_id
WHERE note_tag.user_id = ? WHERE note_tag.user_id = ?
` `

BIN
staticFiles/assets/favicon.ico Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 41 KiB