Added rate limiting and server security
Ton of little visual style tweaks and little up improvements for mobile
This commit is contained in:
		| @@ -6,7 +6,7 @@ mkdir -p $BACKUPDIR | ||||
| cd $BACKUPDIR | ||||
|  | ||||
| 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" | ||||
|  | ||||
|   | ||||
| @@ -18,14 +18,14 @@ tar -czf release.tar.gz server node_modules client/dist staticFiles/assets | ||||
|  | ||||
| #send compressed release to remote machine | ||||
| 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 | ||||
| rm release.tar.gz | ||||
|  | ||||
| #uncompress release on server | ||||
| 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 | ||||
| echo -e "\e[32m\nRelease Complete! Nice Work! \n\e[0m" | ||||
| @@ -1,5 +1,5 @@ | ||||
| <template> | ||||
| 	<div id="app"> | ||||
| 	<div id="app" :class="{ 'night-mode':($store.getters.getIsNightMode) }"> | ||||
|  | ||||
| 		<global-site-menu /> | ||||
|  | ||||
|   | ||||
| @@ -35,6 +35,14 @@ div.ui.basic.segment.no-fluf-segment { | ||||
| 		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*/ | ||||
| body { | ||||
| 	color: var(--text_color); | ||||
| @@ -231,22 +239,27 @@ a:hover { | ||||
| 	.squire-box a { | ||||
| 		cursor: pointer; | ||||
| 	} | ||||
| /*	.note-card-text i, | ||||
| 	.note-card-text i:not(.icon), | ||||
| 	.squire-box i { | ||||
| 		padding: 0.5em 0.99em; | ||||
| 		border: 1px solid #CCC; | ||||
| 		margin: 1px; | ||||
| 		border-radius: 9px; | ||||
| 		border-radius: 1px; | ||||
| 		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 { | ||||
| 		margin-bottom: 0; | ||||
| 	} | ||||
| 	.note-card-text blockquote,  | ||||
| 	.squire-box blockquote { | ||||
| 		margin: 0; | ||||
| 		padding: 0.8em; | ||||
| 		border-left: 2px solid blue; | ||||
| 		padding: 0 0 0 2.5em; | ||||
| 	} | ||||
| 	.note-card-text img { | ||||
| 		max-width:100%; | ||||
| @@ -305,6 +318,10 @@ a:hover { | ||||
| 	/* adjust checkboxes for mobile. Make them a little bigger, easier to click */ | ||||
| 	@media only screen and (max-width: 740px) { | ||||
|  | ||||
| 		.ui.button.shrinking { | ||||
| 			font-size: 0.85714286rem; | ||||
| 		} | ||||
|  | ||||
| 		.note-card-text ul > li, | ||||
| 		.squire-box ul > li { | ||||
| 			min-height: 30px; | ||||
| @@ -346,6 +363,12 @@ a:hover { | ||||
| .float-right { | ||||
| 	float: right; | ||||
| } | ||||
| .ui.grid.reduced-padding > .column { | ||||
| 	padding-left: 2px; | ||||
| 	padding-right: 2px; | ||||
| 	padding-top: 5px; | ||||
| 	padding-bottom: 5px; | ||||
| } | ||||
|  | ||||
| .textarea-height { | ||||
| 	height: calc(100% - 90px); | ||||
|   | ||||
| @@ -18,6 +18,11 @@ | ||||
| 		left: 0; | ||||
| 		bottom: 0; | ||||
| 	} | ||||
| 	.menu-logo-display { | ||||
| 		width: 25px; | ||||
| 		margin: 5px 0 0 34px; | ||||
| 		display: inline-block; | ||||
| 	} | ||||
|  | ||||
| 		.menu-item { | ||||
| 			color: #fff; | ||||
| @@ -101,31 +106,39 @@ | ||||
| 						<i class="green bars icon"></i> | ||||
| 					</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> | ||||
| 					</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"> | ||||
| 						<i class="green moon outline icon"></i> | ||||
| 					</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> | ||||
| 				</div> | ||||
| 				<div class="three wide right aligned column"> | ||||
|  | ||||
| 					<!-- mobile create note button --> | ||||
| 					<div v-if="loggedIn"> | ||||
| 						<div v-if="!disableNewNote" @click="createNote" class="ui large basic compact icon button"> | ||||
| 					<span v-if="loggedIn"> | ||||
| 						<span v-if="!disableNewNote" @click="createNote" class="ui large basic compact icon button"> | ||||
| 							<i class="green plus icon"></i> | ||||
| 						</div> | ||||
| 						<div v-if="disableNewNote" class="ui large basic compact icon button"> | ||||
| 						</span> | ||||
| 						<span v-if="disableNewNote" class="ui large basic compact icon button"> | ||||
| 							<i class="grey plus icon"></i> | ||||
| 						</div> | ||||
| 					</div> | ||||
|  | ||||
| 						</span> | ||||
| 					</span> | ||||
| 				</div> | ||||
|  | ||||
| 			</div> | ||||
| 		</div> | ||||
|  | ||||
| @@ -136,10 +149,11 @@ | ||||
|  | ||||
| 		<div class="global-menu" v-if="!collapsed" v-on:click="menuClicked"> | ||||
|  | ||||
| 			<div class="menu-section"> | ||||
| 				<div class="menu-item menu-button" v-on:click="collapseMenu"> | ||||
| 					<i class="angle left icon"></i> | ||||
| 				</div> | ||||
| 			<div class="menu-section" v-on:click="collapseMenu"> | ||||
| 				<!-- <div class="menu-item menu-button" > --> | ||||
| 					<i class="white angle left icon"></i> | ||||
| 					<img class="menu-logo-display" loading="lazy" src="/api/static/assets/logo.svg" alt="Solid Scribe Logo"> | ||||
| 				<!-- </div> --> | ||||
| 			</div> | ||||
|  | ||||
| 			<div class="menu-section" v-if="loggedIn"> | ||||
|   | ||||
| @@ -19,23 +19,24 @@ | ||||
|  | ||||
| 		<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> | ||||
|  | ||||
| 		<!-- Squire box grows --> | ||||
| @@ -92,25 +93,52 @@ | ||||
|  | ||||
| 			<!-- bottom stats --> | ||||
| 			<div class="ui basic segment"> | ||||
| 				<div class="ui grid"> | ||||
| 					<div class="sixteen wide column"> | ||||
| 				<div class="ui grid reduced-padding"> | ||||
|  | ||||
| 					<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"> | ||||
| 						 | ||||
| 						<div class="ui basic button"v-if="!isEncrypted" v-on:click="passwordEnterVisible = true"> | ||||
| 						<button class="ui basic compact button shrinking" v-if="!isEncrypted" v-on:click="passwordEnterVisible = true"> | ||||
| 							<i class="shield alternate icon"></i> | ||||
| 							Password Protect | ||||
| 						</div> | ||||
| 						<div class="ui icon basic button" v-if="isEncrypted && isDecrypted" v-on:click="disableEncryption"> | ||||
| 						</button> | ||||
| 						<button class="ui icon basic compact button shrinking" v-if="isEncrypted && isDecrypted" v-on:click="disableEncryption"> | ||||
| 							<i class="unlock icon"></i> | ||||
| 							Remove Password | ||||
| 						</div> | ||||
| 						 | ||||
| 						<div class="ui basic compact button"> | ||||
| 							Status: {{ statusText }} | ||||
| 						</div> | ||||
| 						<div class="ui basic compact button" :data-tooltip="`Created: ${$helpers.timeAgo(created)}`"> | ||||
| 							Last Change: {{ $helpers.timeAgo(updated) }} | ||||
| 						</div> | ||||
| 						</button> | ||||
| 					</div> | ||||
| 					<div class="seven wide right aligned middle aligned column"> | ||||
| 						<span :data-tooltip="`Created: ${$helpers.timeAgo(created)}`"> | ||||
| 							Edited: {{ $helpers.timeAgo(updated) }}  | ||||
| 						</span> | ||||
| 					</div> | ||||
| 					<div class="one wide column"></div> | ||||
| 				</div> | ||||
| 			</div> | ||||
|  | ||||
| @@ -126,31 +154,13 @@ | ||||
| 		<div class="all-settings" :class="{ 'low-settings':!extraToolbarsVisible }"> | ||||
|  | ||||
| 			<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 --> | ||||
| 				<nm-button | ||||
| 					v-on:click.native="showColorPicker" | ||||
| 					icon="paint brush" | ||||
| 					text="Colors" | ||||
| 					tip="Colors" | ||||
| 					text="Color" | ||||
| 					tip="Note Color" | ||||
| 				></nm-button> | ||||
|  | ||||
| 				<!-- add images panel --> | ||||
| @@ -161,27 +171,18 @@ | ||||
| 					tip="Images" | ||||
| 				></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  | ||||
| 					class="nm-button"  | ||||
| 					:noteId="noteid" /> | ||||
|  | ||||
| 				<!-- files button --> | ||||
| 				<nm-button | ||||
| 					v-on:click.native="openEditAttachment" | ||||
| 					icon="folder" | ||||
| 					text="Files" | ||||
| 					tip="Files on Note" | ||||
| 					:showText="true" | ||||
| 				></nm-button> | ||||
| 				<nm-button v-on:click.native="undoCustom()" icon="undo" tip="Undo" text="Undo" /> | ||||
|  | ||||
| 				<nm-button  | ||||
| 					icon="ellipsis horizontal"  | ||||
| 					text="Options" | ||||
| 					tip="More Options" | ||||
| 					v-on:click.native="showNoteOptions = !showNoteOptions" /> | ||||
|  | ||||
| 			</div> | ||||
| 		</div> | ||||
| @@ -238,12 +239,6 @@ | ||||
| 							Uncheck all Checked items | ||||
| 						</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="ui labeled icon fluid basic button" v-on:click="calculateMath" data-tooltip="Calculates algebra before '='"> | ||||
| 							<i class="calculator icon"></i> | ||||
| @@ -251,6 +246,7 @@ | ||||
| 						</div> | ||||
| 					</div> | ||||
| 					<div class="sixteen wide column" v-if="rawTextId > 0"> | ||||
| 						<h2>Share Note</h2> | ||||
| 						<share-note-component  | ||||
| 							:note-id="noteid" | ||||
| 							:raw-text-id="rawTextId" | ||||
| @@ -274,7 +270,7 @@ | ||||
| 				<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><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> | ||||
|  | ||||
| 				</div> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <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  --> | ||||
| 		<i v-if="icon" :class="`${icon} icon`"></i> | ||||
| 		<span v-if="(text && mobile) || (text && showText)">{{text}}</span> | ||||
| @@ -21,12 +21,11 @@ | ||||
|  | ||||
| 	export default { | ||||
| 		name: 'NoteMenuButtonComponent', | ||||
| 		props: [ 'icon', 'text', 'tooltip', 'moreClass', 'showText', 'tip'], | ||||
| 		props: [ 'icon', 'text', 'tooltip', 'moreClass', 'showText', 'tip', 'bottomTip'], | ||||
| 		data () { | ||||
| 			return { | ||||
| 				files: [], | ||||
| 				mobile: false, | ||||
| 				showTooltip: false, | ||||
| 			} | ||||
| 		}, | ||||
| 		beforeMount(){ | ||||
|   | ||||
| @@ -1,10 +1,46 @@ | ||||
| <style type="text/css" scoped> | ||||
| 	.fixed-search { | ||||
| 		position: fixed; | ||||
| 		top: 50%; | ||||
| 		left: 0; | ||||
| 		right: 0; | ||||
| 		padding: 10px; | ||||
| 	} | ||||
| </style> | ||||
| <template> | ||||
| 	<div class="ui form"> | ||||
| 		<div class="ui left icon fluid input"> | ||||
| 			<input v-model="searchTerm" @keyup="searchKeyUp" @:keyup.enter="search" placeholder="Search Notes and Files" /> | ||||
| 			<i class="search icon"></i> | ||||
| 	<span> | ||||
|  | ||||
| 		<div class="ui form" v-if="!$store.getters.getIsUserOnMobile"> | ||||
| 			<!-- normal search menu  --> | ||||
| 			<div class="ui left icon fluid input"> | ||||
| 				<input v-model="searchTerm" @keyup="searchKeyUp" @keyup.enter="search" placeholder="Search Notes and Files" ref="searchInput"/> | ||||
| 				<i class="search icon"></i> | ||||
| 			</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> | ||||
|  | ||||
| <script> | ||||
| @@ -16,6 +52,7 @@ | ||||
| 				searchTerm: '', | ||||
| 				searchTimeout: null, | ||||
| 				searchDebounceDuration: 300, | ||||
| 				showFixedSearch: false, | ||||
| 			} | ||||
| 		}, | ||||
| 		beforeCreate: function(){ | ||||
| @@ -29,13 +66,27 @@ | ||||
|  | ||||
| 		}, | ||||
| 		methods: { | ||||
| 			openFloatingSearch(){ | ||||
| 				this.showFixedSearch = !this.showFixedSearch | ||||
|  | ||||
| 				if(this.showFixedSearch){ | ||||
| 					this.$nextTick( () => { | ||||
| 						this.searchTerm = '' | ||||
| 						this.$refs.fixedSearch.focus() | ||||
| 					}) | ||||
| 				} | ||||
| 			}, | ||||
| 			searchKeyUp(){ | ||||
| 				//This event is not triggered on mobile | ||||
| 				clearTimeout(this.searchTimeout) | ||||
| 				this.searchTimeout = setTimeout(() => { | ||||
| 					this.search() | ||||
| 				}, this.searchDebounceDuration) | ||||
| 			}, | ||||
| 			search(){ | ||||
| 				if(this.$store.getters.getIsUserOnMobile){ | ||||
| 					this.$refs.fixedSearch.blur() | ||||
| 				} | ||||
| 				this.$bus.$emit('update_search_term', this.searchTerm) | ||||
| 			}, | ||||
| 		} | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| <template> | ||||
| 	<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"> | ||||
| 					<i class="folder open outline icon"></i> | ||||
| 						<div class="content"> | ||||
| @@ -14,7 +14,7 @@ | ||||
| 				<!-- subnav  --> | ||||
| 				<router-link | ||||
| 					exact-active-class="green"  | ||||
| 					class="ui basic button"  | ||||
| 					class="ui basic button shrinking"  | ||||
| 					to="/attachments"> | ||||
| 					<i class="open folder outline icon"></i> | ||||
| 					All | ||||
| @@ -22,7 +22,7 @@ | ||||
| 				<router-link | ||||
| 					v-if="$store.getters.totals && $store.getters.totals['linkFiles']" | ||||
| 					exact-active-class="green"  | ||||
| 					class="ui basic button"  | ||||
| 					class="ui basic button shrinking"  | ||||
| 					to="/attachments/type/links"> | ||||
| 					<i class="linkify icon"></i> | ||||
| 					Links | ||||
| @@ -30,16 +30,13 @@ | ||||
| 				<router-link  | ||||
| 					v-if="$store.getters.totals && $store.getters.totals['otherFiles']" | ||||
| 					exact-active-class="green" | ||||
| 					class="ui basic button" | ||||
| 					class="ui basic button shrinking" | ||||
| 					to="/attachments/type/files"> | ||||
| 					<i class="copy icon"></i> | ||||
| 					Other Files | ||||
| 				</router-link> | ||||
|  | ||||
| 			</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"> | ||||
| 				<router-link class="ui green button" to="/attachments"> | ||||
| @@ -50,6 +47,8 @@ | ||||
| 					<i class="file outline icon"></i> | ||||
| 					Open Note | ||||
| 				</div> | ||||
|  | ||||
| 				<i v-if="loading" class="green sync alternate loading icon"></i> | ||||
| 			</div> | ||||
|  | ||||
| 			<div class="sixteen wide column" v-if="searchParams['noteId'] && attachments.length == 0"> | ||||
|   | ||||
| @@ -11,12 +11,16 @@ | ||||
| 		-moz-animation: fadeorama 16s ease infinite; | ||||
| 		animation: fadeorama 16s ease infinite; | ||||
| 	} | ||||
| 	.logo-display { | ||||
| 		width: 50%; | ||||
| 	} | ||||
| 	.lightly-padded { | ||||
| 		margin-top: 10px; | ||||
| 	} | ||||
| 	.massive-text { | ||||
| 		color: white; | ||||
| 		font-size: 4rem; | ||||
| 		text-align: center; | ||||
| 	} | ||||
| 	.blinking { | ||||
| 		animation:blinkingText 1.5s linear infinite; | ||||
| @@ -63,6 +67,9 @@ | ||||
| 	} | ||||
|  | ||||
| 	/*safari fix - prevents page from being below the menu */ | ||||
| 	.green-text { | ||||
| 		color: #3710a4; | ||||
| 	} | ||||
| 	.dont-pad-me { | ||||
| 		margin-right: 0 !important; | ||||
| 		margin-left: 0 !important; | ||||
| @@ -101,13 +108,18 @@ | ||||
|  | ||||
| 				<!-- desktop column - large screen only --> | ||||
| 				<div class="seven wide middle aligned left aligned column"> | ||||
| 					<h2 class="massive-text">Take Notes, <br>Like Never Before</h2> | ||||
|  | ||||
| 					<h2 class="massive-text"> | ||||
| 						<img class="logo-display" loading="lazy" src="/api/static/assets/logo.svg" alt="Solid Scribe Logo"> | ||||
| 						<br> | ||||
| 						Solid Scribe | ||||
| 					</h2> | ||||
|  | ||||
| 					<h3 class="subtext"> | ||||
| 						Using an online note application <i class="i cursor icon blinking"></i>  | ||||
| 						Take Notes Like Never Before<i class="i cursor icon blinking"></i>  | ||||
| 					</h3> | ||||
| 					<p>Assuming you have never used a note application previously in your life.</p> | ||||
| 					<br> | ||||
| 					<i class="huge inverted chevron circle down icon"></i> | ||||
| 					<p class="green-text">Assuming you have never used a note application previously in your life.</p> | ||||
| 					 | ||||
| 				</div> | ||||
|  | ||||
| 				<div class="eight wide middle aligned left aligned column"> | ||||
| @@ -269,7 +281,6 @@ | ||||
| 					<p> | ||||
| 						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>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> | ||||
| 				</div> | ||||
| 				<div class="four wide column"> | ||||
| @@ -291,15 +302,14 @@ export default { | ||||
| 			realInformation: false, | ||||
| 		} | ||||
| 	}, | ||||
| 	beforeMount(){ | ||||
|  | ||||
| 		 | ||||
|  | ||||
| 	beforeCreate(){ | ||||
| 		//Force HTTPS on prod, always. Dev doesn't have certs | ||||
| 		const isDev = process.env['NODE_ENV'] == 'development' | ||||
| 		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 | ||||
| 		if(!this.$store.getters.getIsUserOnMobile){ | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
| 					 | ||||
| 					<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-if="$store.getters.totals && ($store.getters.totals['sharedToNotes'] > 0 || $store.getters.totals['sharedFromNotes'] > 0)"  | ||||
| 						style="position: relative;"> | ||||
| @@ -24,12 +24,12 @@ | ||||
| 							</span> | ||||
| 						</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 | ||||
| 							<!-- <span>{{ $store.getters.totals['archivedNotes'] }}</span> --> | ||||
| 						</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 | ||||
| 							<!-- <span>{{ $store.getters.totals['encryptedNotes'] }}</span> --> | ||||
| 						</div> | ||||
|   | ||||
| @@ -12,8 +12,11 @@ | ||||
|     "body-parser": "^1.18.3", | ||||
|     "cheerio": "^1.0.0-rc.3", | ||||
|     "express": "^4.16.4", | ||||
|     "express-rate-limit": "^5.1.1", | ||||
|     "gm": "^1.23.1", | ||||
|     "helmet": "^3.21.3", | ||||
|     "jsonwebtoken": "^8.5.1", | ||||
|     "module-alias": "^2.2.2", | ||||
|     "multer": "^1.4.2", | ||||
|     "mysql2": "^1.6.5", | ||||
|     "node-tesseract-ocr": "^1.0.0", | ||||
|   | ||||
| @@ -26,6 +26,17 @@ ProcessText.stripBlankHtmlLines = (string) => { | ||||
| 	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) => { | ||||
| 	const urlPattern = /(?:(?:https?|ftp|file):\/\/|www\.|ftp\.)(?:\([-A-Z0-9+&@#/%=~_|$?!:,.]*\)|[-A-Z0-9+&@#/%=~_|$?!:,.])*(?:\([-A-Z0-9+&@#/%=~_|$?!:,.]*\)|[A-Z0-9+&@#/%=~_|$])/igm | ||||
| 	return string.match(urlPattern) | ||||
| @@ -41,11 +52,16 @@ ProcessText.getUrlsFromString = (string) => { | ||||
| 		+ 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 | ||||
|  | ||||
| 	//Always return a title as a String | ||||
| 	if(title == null){ | ||||
| 		title = '' | ||||
| 	} | ||||
|  | ||||
| 	if(!inString || inString == null || inString.length == 0){ | ||||
| 		return {title, sub} | ||||
| 	} | ||||
| @@ -55,16 +71,17 @@ ProcessText.deduceNoteTitle = (inString) => { | ||||
|  | ||||
| 	const tagFreeLength = ProcessText.removeHtml(inString).length | ||||
|  | ||||
| 	if(tagFreeLength < 100){ | ||||
| 		sub = ProcessText.stripBlankHtmlLines(inString) | ||||
| 		return {title, sub} | ||||
| 	// | ||||
| 	// Simplified attempt! | ||||
| 	// 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 | ||||
| 	inString += '</end>' | ||||
| @@ -87,6 +104,7 @@ ProcessText.deduceNoteTitle = (inString) => { | ||||
| 	let charLimit = 400 | ||||
| 	let listStart = false | ||||
| 	let noTitleJustList = false | ||||
| 	let appendCaret = false | ||||
|  | ||||
| 	for(let i=0; i < totalLines; i++){ | ||||
|  | ||||
| @@ -167,8 +185,8 @@ ProcessText.deduceNoteTitle = (inString) => { | ||||
| 			if(cleanCutString.length == 0){ | ||||
| 				cleanCutString = cutString | ||||
| 			} | ||||
|  | ||||
| 			finalLines.push(cleanCutString + '... <i class="green caret down icon"></i>') | ||||
| 			appendCaret = true | ||||
| 			 | ||||
| 			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 | ||||
| 	if(ProcessText.removeHtml(finalLines[0]).trim().replace(' ','').length > 0 && !noTitleJustList){ | ||||
| 		// title = finalLines.shift() | ||||
| 	if(!noTitleJustList && title == ''){ | ||||
| 		title = ProcessText.removeHtml( finalLines.shift() ).replace(' ','') | ||||
| 	} | ||||
| 	 | ||||
| 	sub = finalLines.join('') | ||||
|   | ||||
| @@ -3,10 +3,29 @@ require('module-alias/register') | ||||
|  | ||||
| let Auth = require('@helpers/Auth') | ||||
|  | ||||
| const helmet = require('helmet') | ||||
|  | ||||
|  | ||||
| const express = require('express') | ||||
| const app = express() | ||||
| app.use( helmet() ) | ||||
| 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 io = require('socket.io')(http, { | ||||
| 	path:'/socket' | ||||
|   | ||||
| @@ -512,7 +512,7 @@ Note.solrQuery = (userId, searchQuery, searchTags) => { | ||||
| 		} else { | ||||
|  | ||||
| 			//Number of characters before and after search word | ||||
| 			const front = 5 | ||||
| 			const front = 20 | ||||
| 			const tail = 150 | ||||
|  | ||||
| 			db.promise() | ||||
| @@ -584,7 +584,7 @@ Note.search = (userId, searchQuery, searchTags, fastFilters) => { | ||||
| 			let searchParams = [userId] | ||||
| 			let noteSearchQuery = ` | ||||
| 				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.updated as updated,  | ||||
| 					opened, | ||||
| @@ -722,15 +722,10 @@ Note.search = (userId, searchQuery, searchTags, fastFilters) => { | ||||
| 					if(note.encrypted == 1){ note.text = '' } | ||||
|  | ||||
| 					//Deduce note title | ||||
| 					const textData = ProcessText.deduceNoteTitle(note.text) | ||||
| 					const textData = ProcessText.deduceNoteTitle(note.title, note.text) | ||||
| 					// console.log(textData) | ||||
|  | ||||
| 					// console.log(textData) | ||||
| 						 | ||||
| 					if(note.title == null){ | ||||
| 						note.title = '' | ||||
| 					} | ||||
| 					 | ||||
| 					note.title = textData.title | ||||
| 					note.subtext = textData.sub | ||||
| 					note.titleLength = textData.titleLength | ||||
| 					note.subtextLength = textData.subtextLength | ||||
|   | ||||
| @@ -5,6 +5,10 @@ let Tag = module.exports = {} | ||||
| Tag.userTags = (userId, searchQuery, searchTags, fastFilters) => { | ||||
| 	return new Promise((resolve, reject) => { | ||||
|  | ||||
| 		if(searchQuery && searchQuery.length > 0){ | ||||
| 			return resolve([]) | ||||
| 		} | ||||
|  | ||||
| 		let query = ` | ||||
| 			SELECT  | ||||
| 				tag.id,  | ||||
| @@ -12,7 +16,7 @@ Tag.userTags = (userId, searchQuery, searchTags, fastFilters) => { | ||||
| 				COUNT(note_tag.note_id) as usages | ||||
| 			FROM tag | ||||
| 			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 = ? | ||||
| 		` | ||||
|  | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								staticFiles/assets/favicon.ico
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										
											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 | 
		Reference in New Issue
	
	Block a user