* Added fake site warning
* Fixed a bunch of style bugs for chrome browsers * Improved check box styles on desktop and mobile * Touch up tool tip styles. Only dark now. * Created a separate terms page * Added 2FA auth token options to login * Added tool tip displays to some buttons on editor * Added pinned and archived options to overflow menu * Changed shared note styles * Disabled Scroll into view * Made image display smaller when adding images to notes * Added a last used color option * Updated help page * Fixed spelling error on terms page * Added a big ass green label on the new note icon * Scratch pad now opens a note, which is the scratch pad * Added better 2fa guide * Added change password option * Added log out and log out all active sessions option * Added strict rate limiting on login and register actions * Added middleware to routes that force authentication to be accessed * Fixed bug that was causing shared notes to appear empty * Updated option now appears on shared notes after they are actually updated
This commit is contained in:
1 parent
a8a966866c
commit
b34a62e114
24 files changed
+559
-483
No files matched your search
+28
-3
@@ -1,9 +1,22 @@
|
||||
<template>
|
||||
<div id="app" :class="{ 'night-mode':($store.getters.getIsNightMode == 2) }">
|
||||
|
||||
<global-site-menu />
|
||||
<div class="ui container" v-if="showFakeSite">
|
||||
<div class="ui basic very padded segment">
|
||||
<div class="ui inverted red segment">
|
||||
<h1>WARNING - False site detected</h1>
|
||||
<h2>The Domain for this website is not correct.</h2>
|
||||
<h2>Only trust <a class="ui button" href="https://www.solidscribe.com">https://www.solidscribe.com</a></h2>
|
||||
<h2>Do not any enter any personal information into this website.</h2>
|
||||
<h2>You will be redirected to the correct domain in {{redirectSeconds}}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<router-view />
|
||||
|
||||
<global-site-menu v-if="!showFakeSite" />
|
||||
|
||||
<router-view v-if="!showFakeSite" />
|
||||
|
||||
<global-notification />
|
||||
|
||||
@@ -22,7 +35,8 @@ export default {
|
||||
},
|
||||
data: function(){
|
||||
return {
|
||||
// loggedIn:
|
||||
showFakeSite:false, //Incorrect domain detection
|
||||
redirectSeconds: 15,
|
||||
fetchingInProgress: false, //Prevent start getting token while fetch is in progress
|
||||
blockUntilNextRequest: false //If token was just renewed, don't fetch more until next request
|
||||
}
|
||||
@@ -112,6 +126,17 @@ export default {
|
||||
},
|
||||
mounted: function(){
|
||||
|
||||
const isDev = process.env['NODE_ENV'] == 'development'
|
||||
if(window.location.hostname.toLowerCase() != "www.solidscribe.com" && !isDev){
|
||||
this.showFakeSite = true
|
||||
setInterval(() => {
|
||||
this.redirectSeconds--
|
||||
if(this.redirectSeconds == 0){
|
||||
window.location.href = 'https://www.solidscribe.com'
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
//Update totals for entire app on event
|
||||
this.$io.on('update_counts', () => {
|
||||
console.log('Got event, update totals')
|
||||
|
||||
@@ -116,6 +116,9 @@ body {
|
||||
background-color: transparent;
|
||||
border-color: var(--dark_border_color);
|
||||
}
|
||||
.ui.dividing.header {
|
||||
border-bottom-color: var(--dark_border_color);
|
||||
}
|
||||
.ui.icon.input > i.icon {
|
||||
color: var(--text_color);
|
||||
}
|
||||
@@ -251,6 +254,13 @@ i.green.icon.icon.icon.icon {
|
||||
z-index: 100;
|
||||
cursor: pointer;
|
||||
}
|
||||
.text-container {
|
||||
max-width: 1000px;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
background-color: var(--small_element_bg_color) !important;
|
||||
}
|
||||
|
||||
/* squire text styles */
|
||||
.squire-box {
|
||||
@@ -264,7 +274,7 @@ i.green.icon.icon.icon.icon {
|
||||
box-sizing: border-box;
|
||||
padding: 10px 15px 10px;
|
||||
/*background: transparent;*/
|
||||
overflow-x: scroll;
|
||||
overflow: hidden;
|
||||
font-size: 1.2em;
|
||||
line-height: 1.8em;
|
||||
word-wrap: break-word;
|
||||
@@ -295,7 +305,7 @@ i.green.icon.icon.icon.icon {
|
||||
|
||||
.note-card-text pre,
|
||||
.squire-box pre {
|
||||
word-wrap: break-word;
|
||||
/*word-wrap: break-word;*/
|
||||
}
|
||||
.note-card-text p,
|
||||
.squire-box p {
|
||||
@@ -307,6 +317,10 @@ i.green.icon.icon.icon.icon {
|
||||
margin: 0;
|
||||
padding: 0 0 0 2.5em;
|
||||
}
|
||||
.note-card-text u,
|
||||
.squire-box u {
|
||||
text-decoration-color: var(--main-accent);
|
||||
}
|
||||
.note-card-text img {
|
||||
max-width:100%;
|
||||
height: auto;
|
||||
@@ -322,7 +336,12 @@ i.green.icon.icon.icon.icon {
|
||||
.squire-box li > p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.note-card-text ol,
|
||||
.squire-box ol,
|
||||
.note-card-text ul,
|
||||
.squire-box ul {
|
||||
margin: 8px 0 0 0;
|
||||
}
|
||||
.note-card-text ul > li,
|
||||
.squire-box ul > li {
|
||||
position: relative;
|
||||
@@ -331,35 +350,53 @@ i.green.icon.icon.icon.icon {
|
||||
.note-card-text ul > li:before,
|
||||
.squire-box ul > li:before {
|
||||
|
||||
/*filled circle */
|
||||
content: "\f111";
|
||||
/*empty square*/
|
||||
/*content: "\F0C8";*/
|
||||
|
||||
font-family: 'Icons';
|
||||
/*font-family: 'outline-icons';*/
|
||||
backface-visibility: hidden;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
text-decoration: inherit;
|
||||
text-align: center;
|
||||
line-height: 1.4em;
|
||||
font-size: 0.75em;
|
||||
font-size: 0.25em;
|
||||
|
||||
height: 17px;
|
||||
width: 17px;
|
||||
height: 100%;
|
||||
width: 20px;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
|
||||
left: -30px;
|
||||
left: -25px;
|
||||
/*border: 2px solid #444;*/
|
||||
/*border-radius: 4px;*/
|
||||
bottom: 0;
|
||||
top: 4px;
|
||||
top: 0;
|
||||
cursor: pointer;
|
||||
opacity: 0.7;
|
||||
|
||||
color: var(--text_color);
|
||||
text-align: center;
|
||||
}
|
||||
/* filled in check circle */
|
||||
ul > li.active:before {
|
||||
|
||||
font-family: 'Icons';
|
||||
content: "\f058";
|
||||
content: "\F14A";
|
||||
color: var(--main-accent);
|
||||
opacity: 1;
|
||||
|
||||
font-size: 1em;
|
||||
}
|
||||
/* hover - transparent icon */
|
||||
.squire-box ul > li:hover:not(.active):before {
|
||||
font-family: 'outline-icons';
|
||||
content: "\f14a";
|
||||
opacity: 0.4;
|
||||
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.note-title-display-card .divide,
|
||||
@@ -447,27 +484,28 @@ i.green.icon.icon.icon.icon {
|
||||
min-height: 30px;
|
||||
}
|
||||
|
||||
/*empty check box*/
|
||||
.note-card-text ul > li:before,
|
||||
.squire-box ul > li:before {
|
||||
.squire-box ul > li:before,
|
||||
.squire-box ul > li:hover:not(.active):before {
|
||||
|
||||
/*empty checkmark*/
|
||||
/*font-family: 'Icons';*/
|
||||
/*content: "\f058";*/
|
||||
|
||||
content: "\f111";
|
||||
font-family: outline-icons;
|
||||
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
content: "\F0C8";
|
||||
font-family: 'outline-icons';
|
||||
|
||||
left: -40px;
|
||||
bottom: 0;
|
||||
top: 0px;
|
||||
cursor: pointer;
|
||||
|
||||
line-height: 0.9em;
|
||||
font-size: 1.4em;
|
||||
opacity: 0.2;
|
||||
}
|
||||
/*Filled check box */
|
||||
ul > li.active:before {
|
||||
|
||||
font-family: 'Icons';
|
||||
content: "\f058";
|
||||
content: "\F14A";
|
||||
|
||||
color: var(--main-accent);
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -539,36 +577,34 @@ i.green.icon.icon.icon.icon {
|
||||
position: absolute;
|
||||
content: '';
|
||||
font-size: 1rem;
|
||||
width: 0.71428571em;
|
||||
height: 0.71428571em;
|
||||
background: #FFFFFF;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: #1B1C1D;
|
||||
-webkit-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
z-index: 1901;
|
||||
-webkit-box-shadow: 1px 1px 0 0 #bababc;
|
||||
box-shadow: 1px 1px 0 0 #bababc;
|
||||
}
|
||||
|
||||
/* Popup */
|
||||
[data-tooltip]:after {
|
||||
min-width: 40px;
|
||||
pointer-events: none;
|
||||
content: attr(data-tooltip);
|
||||
position: absolute;
|
||||
text-transform: none;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
white-space: pre;
|
||||
font-size: 1rem;
|
||||
border: 1px solid #D4D4D5;
|
||||
line-height: 1.4285em;
|
||||
max-width: none;
|
||||
background: #FFFFFF;
|
||||
padding: 0.833em 1em;
|
||||
background: #1B1C1D;
|
||||
padding: 0.5em;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
/*color: var(--main-accent);*/
|
||||
color: white;
|
||||
border-radius: 0.28571429rem;
|
||||
-webkit-box-shadow: 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15);
|
||||
box-shadow: 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15);
|
||||
z-index: 1900;
|
||||
}
|
||||
|
||||
@@ -578,7 +614,7 @@ i.green.icon.icon.icon.icon {
|
||||
right: auto;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
background: #FFFFFF;
|
||||
background: #1B1C1D;
|
||||
margin-left: -0.07142857rem;
|
||||
margin-bottom: 0.14285714rem;
|
||||
}
|
||||
@@ -596,7 +632,7 @@ i.green.icon.icon.icon.icon {
|
||||
pointer-events: none;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
/*transition: opacity 0.2s ease;*/
|
||||
}
|
||||
[data-tooltip]:before {
|
||||
-webkit-transform: rotate(45deg) scale(0) !important;
|
||||
@@ -619,93 +655,13 @@ i.green.icon.icon.icon.icon {
|
||||
transform: rotate(45deg) scale(1) !important;
|
||||
}
|
||||
|
||||
/* Animation Position */
|
||||
[data-tooltip]:after,
|
||||
[data-tooltip][data-position="top center"]:after,
|
||||
[data-tooltip][data-position="bottom center"]:after {
|
||||
-webkit-transform: translateX(-50%) scale(0) !important;
|
||||
transform: translateX(-50%) scale(0) !important;
|
||||
}
|
||||
[data-tooltip]:hover:after,
|
||||
[data-tooltip][data-position="bottom center"]:hover:after {
|
||||
-webkit-transform: translateX(-50%) scale(1) !important;
|
||||
transform: translateX(-50%) scale(1) !important;
|
||||
}
|
||||
[data-tooltip][data-position="left center"]:after,
|
||||
[data-tooltip][data-position="right center"]:after {
|
||||
-webkit-transform: translateY(-50%) scale(0) !important;
|
||||
transform: translateY(-50%) scale(0) !important;
|
||||
}
|
||||
[data-tooltip][data-position="left center"]:hover:after,
|
||||
[data-tooltip][data-position="right center"]:hover:after {
|
||||
-webkit-transform: translateY(-50%) scale(1) !important;
|
||||
transform: translateY(-50%) scale(1) !important;
|
||||
}
|
||||
[data-tooltip][data-position="top left"]:after,
|
||||
[data-tooltip][data-position="top right"]:after,
|
||||
[data-tooltip][data-position="bottom left"]:after,
|
||||
[data-tooltip][data-position="bottom right"]:after {
|
||||
-webkit-transform: scale(0) !important;
|
||||
transform: scale(0) !important;
|
||||
}
|
||||
[data-tooltip][data-position="top left"]:hover:after,
|
||||
[data-tooltip][data-position="top right"]:hover:after,
|
||||
[data-tooltip][data-position="bottom left"]:hover:after,
|
||||
[data-tooltip][data-position="bottom right"]:hover:after {
|
||||
-webkit-transform: scale(1) !important;
|
||||
transform: scale(1) !important;
|
||||
}
|
||||
[data-tooltip][data-variation~="fixed"]:after {
|
||||
white-space: normal;
|
||||
width: 250px;
|
||||
}
|
||||
[data-tooltip][data-variation*="wide fixed"]:after {
|
||||
width: 350px;
|
||||
}
|
||||
[data-tooltip][data-variation*="very wide fixed"]:after {
|
||||
width: 550px;
|
||||
}
|
||||
@media only screen and (max-width: 767.98px) {
|
||||
[data-tooltip][data-variation~="fixed"]:after {
|
||||
width: 250px;
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------
|
||||
Inverted
|
||||
---------------*/
|
||||
|
||||
|
||||
/* Arrow */
|
||||
[data-tooltip][data-inverted]:before {
|
||||
-webkit-box-shadow: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* Arrow Position */
|
||||
[data-tooltip][data-inverted]:before {
|
||||
background: #1B1C1D;
|
||||
}
|
||||
|
||||
/* Popup */
|
||||
[data-tooltip][data-inverted]:after {
|
||||
background: #1B1C1D;
|
||||
color: #FFFFFF;
|
||||
border: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
[data-tooltip][data-inverted]:after .header {
|
||||
background: none;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
/*--------------
|
||||
Position
|
||||
---------------*/
|
||||
|
||||
[data-position~="top"][data-tooltip]:before {
|
||||
background: #FFFFFF;
|
||||
background: #1B1C1D;
|
||||
}
|
||||
|
||||
/* Top Center */
|
||||
@@ -723,7 +679,7 @@ i.green.icon.icon.icon.icon {
|
||||
right: auto;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
background: #FFFFFF;
|
||||
background: #1B1C1D;
|
||||
margin-left: -0.07142857rem;
|
||||
margin-bottom: 0.14285714rem;
|
||||
}
|
||||
@@ -762,7 +718,7 @@ i.green.icon.icon.icon.icon {
|
||||
margin-bottom: 0.14285714rem;
|
||||
}
|
||||
[data-position~="bottom"][data-tooltip]:before {
|
||||
background: #FFFFFF;
|
||||
background: #1B1C1D;
|
||||
-webkit-box-shadow: -1px -1px 0 0 #bababc;
|
||||
box-shadow: -1px -1px 0 0 #bababc;
|
||||
}
|
||||
@@ -781,7 +737,7 @@ i.green.icon.icon.icon.icon {
|
||||
bottom: auto;
|
||||
right: auto;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
left: 30%;
|
||||
margin-left: -0.07142857rem;
|
||||
margin-top: 0.14285714rem;
|
||||
}
|
||||
@@ -829,9 +785,7 @@ i.green.icon.icon.icon.icon {
|
||||
top: 50%;
|
||||
margin-top: -0.14285714rem;
|
||||
margin-right: -0.07142857rem;
|
||||
background: #FFFFFF;
|
||||
-webkit-box-shadow: 1px -1px 0 0 #bababc;
|
||||
box-shadow: 1px -1px 0 0 #bababc;
|
||||
background: #1B1C1D;
|
||||
}
|
||||
|
||||
/* Right Center */
|
||||
@@ -847,30 +801,9 @@ i.green.icon.icon.icon.icon {
|
||||
top: 50%;
|
||||
margin-top: -0.07142857rem;
|
||||
margin-left: 0.14285714rem;
|
||||
background: #FFFFFF;
|
||||
-webkit-box-shadow: -1px 1px 0 0 #bababc;
|
||||
box-shadow: -1px 1px 0 0 #bababc;
|
||||
background: #1B1C1D;
|
||||
}
|
||||
|
||||
/* Inverted Arrow Color */
|
||||
[data-inverted][data-position~="bottom"][data-tooltip]:before {
|
||||
background: #1B1C1D;
|
||||
-webkit-box-shadow: -1px -1px 0 0 #bababc;
|
||||
box-shadow: -1px -1px 0 0 #bababc;
|
||||
}
|
||||
[data-inverted][data-position="left center"][data-tooltip]:before {
|
||||
background: #1B1C1D;
|
||||
-webkit-box-shadow: 1px -1px 0 0 #bababc;
|
||||
box-shadow: 1px -1px 0 0 #bababc;
|
||||
}
|
||||
[data-inverted][data-position="right center"][data-tooltip]:before {
|
||||
background: #1B1C1D;
|
||||
-webkit-box-shadow: -1px 1px 0 0 #bababc;
|
||||
box-shadow: -1px 1px 0 0 #bababc;
|
||||
}
|
||||
[data-inverted][data-position~="top"][data-tooltip]:before {
|
||||
background: #1B1C1D;
|
||||
}
|
||||
[data-position~="bottom"][data-tooltip]:before {
|
||||
-webkit-transform-origin: center bottom;
|
||||
transform-origin: center bottom;
|
||||
@@ -894,12 +827,4 @@ i.green.icon.icon.icon.icon {
|
||||
[data-position="right center"][data-tooltip]:after {
|
||||
-webkit-transform-origin: left center;
|
||||
transform-origin: left center;
|
||||
}
|
||||
|
||||
/*--------------
|
||||
Basic
|
||||
---------------*/
|
||||
|
||||
[data-tooltip][data-variation~="basic"]:before {
|
||||
display: none;
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
min-height: 50px;
|
||||
min-width: 200px;
|
||||
max-width: calc(100% - 30px);
|
||||
z-index: 1002;
|
||||
z-index: 1020;
|
||||
|
||||
box-shadow: 0px 0px 5px 2px rgba(140,140,140,1);
|
||||
border-radius: 4px;
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
}
|
||||
.place-holder {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
height: 40px;
|
||||
}
|
||||
.logo-display {
|
||||
width: 27px;
|
||||
@@ -273,11 +273,17 @@
|
||||
|
||||
<div class="menu-section">
|
||||
<router-link class="menu-item menu-button" exact-active-class="active" to="/help">
|
||||
<i class="question circle outline icon"></i>Help | Terms
|
||||
<i class="question circle outline icon"></i>Help
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<div class="menu-section" v-if="loggedIn" :data-tooltip="`Settings for ${this.$store.getters.getUsername}`" data-inverted="" data-position="right center">
|
||||
<div class="menu-section">
|
||||
<router-link class="menu-item menu-button" exact-active-class="active" to="/terms">
|
||||
<i class="info circle icon"></i>Terms
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<div class="menu-section" v-if="loggedIn">
|
||||
<router-link class="menu-item menu-button" exact-active-class="active" to="/settings">
|
||||
<i v-if="userIcon" class="cog icon"></i>{{ usernameDisplay }}
|
||||
</router-link>
|
||||
@@ -365,7 +371,6 @@
|
||||
axios.post('/api/quick-note/get')
|
||||
.then( ({data}) => {
|
||||
|
||||
console.log(data)
|
||||
this.$router.push({'path':'/notes/open/'+data.noteId})
|
||||
})
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<div class="sixteen wide column">
|
||||
<span class="small-terms">
|
||||
By signing up you agree to Solid Scribe's
|
||||
<router-link to="/help">
|
||||
<router-link to="/terms">
|
||||
Terms of Use
|
||||
</router-link>
|
||||
</span>
|
||||
@@ -77,7 +77,7 @@
|
||||
</div>
|
||||
<span class="small-terms">
|
||||
By signing up you agree to Solid Scribe's
|
||||
<router-link to="/help">
|
||||
<router-link to="/terms">
|
||||
Terms of Use
|
||||
</router-link>
|
||||
</span>
|
||||
@@ -147,7 +147,7 @@
|
||||
return
|
||||
}
|
||||
|
||||
axios.post('/api/user/register', {'username': this.username, 'password': this.password})
|
||||
axios.post('/api/public/register', {'username': this.username, 'password': this.password})
|
||||
.then(({data}) => {
|
||||
|
||||
if(data == false){
|
||||
@@ -167,7 +167,7 @@
|
||||
return
|
||||
}
|
||||
|
||||
axios.post('/api/user/login', {'username': this.username, 'password': this.password, 'authToken':this.authToken })
|
||||
axios.post('/api/public/login', {'username': this.username, 'password': this.password, 'authToken':this.authToken })
|
||||
.then(({data}) => {
|
||||
|
||||
//Enable 2FA on form
|
||||
@@ -193,7 +193,7 @@
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
this.$bus.$emit('notification', 'Unable to Login - Incorrect Username or Password')
|
||||
this.$bus.$emit('notification', error)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<!-- change class to .master-note-edit to have it popup on the screen -->
|
||||
<div
|
||||
id="InputNotes"
|
||||
class="master-note-edit full-focus position-0"
|
||||
class="master-note-edit"
|
||||
@keyup.esc="closeButtonAction()"
|
||||
>
|
||||
|
||||
@@ -13,47 +13,51 @@
|
||||
<div class="edit-spacer"></div>
|
||||
|
||||
<div class="menu-top-half">
|
||||
<div class="edit-button" v-on:click="closeButtonAction()" data-tooltip="Close" data-position="bottom center" data-inverted>
|
||||
<div class="edit-button" v-on:click="closeButtonAction()" :data-tooltip="`Close\n(ESC)`" data-position="bottom center">
|
||||
<i class="close icon"></i>
|
||||
</div>
|
||||
|
||||
<div class="edit-divide"></div>
|
||||
|
||||
<div class="edit-button" v-on:click="toggleList('ul')" data-tooltip="Task List" data-position="bottom center" data-inverted :class="{'edit-active':activeToDo}">
|
||||
<div class="edit-button" v-on:click="toggleList('ul')" :data-tooltip="`Task List\n(CTRL + SHIFT + 8)`" data-position="bottom center" :class="{'edit-active':activeToDo}">
|
||||
<i class="tasks icon"></i>
|
||||
</div>
|
||||
<div class="edit-button" v-on:click="toggleList('ol')" data-tooltip="Numbered List" data-position="bottom center" data-inverted :class="{'edit-active':activeList}">
|
||||
<div class="edit-button" v-on:click="toggleList('ol')" :data-tooltip="`Ordered List\n(CTRL + SHIFT + 9)`" data-position="bottom center" :class="{'edit-active':activeList}">
|
||||
<i class="list ol icon"></i>
|
||||
</div>
|
||||
|
||||
<div class="edit-divide"></div>
|
||||
|
||||
<div class="edit-button" v-on:click="colorpicker = true" data-tooltip="Text Color" data-position="bottom center" data-inverted :style="{'color':activeColor}">
|
||||
<div class="edit-button" v-on:click="colorpicker = true" data-tooltip="Text Color" data-position="bottom center">
|
||||
<i class="font icon"></i>
|
||||
</div>
|
||||
|
||||
<div class="edit-button" v-on:click="toggleBold()" data-tooltip="Bold" data-position="bottom center" data-inverted :class="{'edit-active':activeBold}">
|
||||
<div v-if="lastUsedColor" class="edit-button" v-on:click="applyLastUsedColor()" data-tooltip="Last Color" data-position="bottom center" :style="{'color':lastUsedColor}">
|
||||
<i class="eye dropper icon"></i>
|
||||
</div>
|
||||
|
||||
<div class="edit-button" v-on:click="toggleBold()" :data-tooltip="`Bold\n(CTRL + b)`" data-position="bottom center" :class="{'edit-active':activeBold}">
|
||||
<i class="bold icon"></i>
|
||||
</div>
|
||||
|
||||
<div class="edit-button" v-on:click="toggleItalic()" data-tooltip="Italic" data-position="bottom center" data-inverted :class="{'edit-active':activeItalics}">
|
||||
<div class="edit-button" v-on:click="toggleItalic()" :data-tooltip="`Italic\n(CRTL + i)`" data-position="bottom center" :class="{'edit-active':activeItalics}">
|
||||
<i class="italic icon"></i>
|
||||
</div>
|
||||
|
||||
<div class="edit-button" v-on:click="toggleUnderline()" data-tooltip="Underline" data-position="bottom center" data-inverted :class="{'edit-active':activeUnderline}">
|
||||
<div class="edit-button" v-on:click="toggleUnderline()" :data-tooltip="`Underline\n(CRTL + u)`" data-position="bottom center" :class="{'edit-active':activeUnderline}">
|
||||
<i class="underline icon"></i>
|
||||
</div>
|
||||
|
||||
<div class="edit-button" v-on:click="modifyFont('1.4em')" data-tooltip="Title" data-position="bottom center" data-inverted :class="{'edit-active':activeTitle}">
|
||||
<div class="edit-button" v-on:click="modifyFont('1.4em')" data-tooltip="Title" data-position="bottom center" :class="{'edit-active':activeTitle}">
|
||||
<i class="text height icon"></i>
|
||||
</div>
|
||||
|
||||
<div class="edit-divide"></div>
|
||||
|
||||
<div class="edit-button" v-on:click="editor.increaseQuoteLevel()" data-tooltip="Indent" data-position="bottom center" data-inverted>
|
||||
<div class="edit-button" v-on:click="editor.increaseQuoteLevel()" :data-tooltip="`Indent\n(TAB)`" data-position="bottom center">
|
||||
<i class="indent icon"></i>
|
||||
</div>
|
||||
<div class="edit-button" v-on:click="editor.decreaseQuoteLevel()" data-tooltip="Outdent" data-position="bottom center" data-inverted>
|
||||
<div class="edit-button" v-on:click="editor.decreaseQuoteLevel()" :data-tooltip="`Un-Indent\n(SHIFT + TAB)`" data-position="bottom center">
|
||||
<i class="outdent icon"></i>
|
||||
</div>
|
||||
|
||||
@@ -61,60 +65,49 @@
|
||||
|
||||
<div class="menu-bottom-half">
|
||||
|
||||
<div class="edit-button" v-on:click="$router.push(`/notes/open/${noteid}/menu/table`)" data-tooltip="Insert Table" data-position="bottom center" data-inverted>
|
||||
<div class="edit-button" v-on:click="$router.push(`/notes/open/${noteid}/menu/table`)" data-tooltip="Insert Table" data-position="bottom center">
|
||||
<i class="border all icon"></i>
|
||||
</div>
|
||||
|
||||
<div class="edit-button" v-on:click="insertDivide()" data-tooltip="Insert Divide" data-position="bottom center" data-inverted>
|
||||
<div class="edit-button" v-on:click="insertDivide()" data-tooltip="Insert Divide" data-position="bottom center">
|
||||
<i class="grip lines icon"></i>
|
||||
</div>
|
||||
|
||||
<div class="edit-button" v-on:click="removeFormatting()" data-tooltip="Remove Formatting" data-position="bottom center" data-inverted>
|
||||
<div class="edit-button" v-on:click="removeFormatting()" data-tooltip="Remove Formatting" data-position="bottom center">
|
||||
<i class="remove format icon"></i>
|
||||
</div>
|
||||
|
||||
<div class="edit-button" v-on:click="undoCustom()" data-tooltip="Undo" data-position="bottom center" data-inverted>
|
||||
<div class="edit-button" v-on:click="undoCustom()" :data-tooltip="`Undo\n(CTRL + z)`" data-position="bottom center">
|
||||
<i class="undo icon"></i>
|
||||
</div>
|
||||
|
||||
<div class="edit-divide"></div>
|
||||
|
||||
<div class="edit-button" v-on:click="$router.push(`/notes/open/${noteid}/menu/colors`)" data-tooltip="Note Color" data-position="bottom center" data-inverted :style="{ 'background-color':styleObject['noteBackground'], 'color':styleObject['noteText']}">
|
||||
<div class="edit-button" v-on:click="$router.push(`/notes/open/${noteid}/menu/colors`)" data-tooltip="Note Color" data-position="bottom center" :style="{ 'background-color':styleObject['noteBackground'], 'color':styleObject['noteText']}">
|
||||
<i class="paint brush icon"></i>
|
||||
</div>
|
||||
<div class="edit-button" v-on:click="$router.push(`/notes/open/${noteid}/menu/tags`)" data-tooltip="Tags" data-position="bottom center" data-inverted>
|
||||
<div class="edit-button" v-on:click="$router.push(`/notes/open/${noteid}/menu/tags`)" data-tooltip="Tags" data-position="bottom center">
|
||||
<i class="tags icon"></i>
|
||||
</div>
|
||||
<div class="edit-button" v-on:click="$router.push(`/notes/open/${noteid}/menu/images`)" data-tooltip="Images" data-position="bottom center" data-inverted>
|
||||
<div class="edit-button" v-on:click="$router.push(`/notes/open/${noteid}/menu/images`)" data-tooltip="Images" data-position="bottom center">
|
||||
<i class="image icon"></i>
|
||||
</div>
|
||||
|
||||
<file-upload-button
|
||||
data-tooltip="Upload File" data-position="bottom center" data-inverted
|
||||
data-tooltip="Upload File" data-position="bottom center"
|
||||
class="edit-button"
|
||||
:noteId="noteid" />
|
||||
|
||||
<div class="edit-divide"></div>
|
||||
|
||||
<div class="edit-button" v-on:click="$router.push(`/notes/open/${noteid}/menu/options`)" data-tooltip="More Options" data-position="bottom center" data-inverted>
|
||||
<div class="edit-button" v-on:click="$router.push(`/notes/open/${noteid}/menu/options`)" data-tooltip="More Options" data-position="bottom center">
|
||||
<i class="ellipsis horizontal icon"></i>
|
||||
</div>
|
||||
|
||||
<div class="edit-divide"></div>
|
||||
|
||||
<!--
|
||||
<div class="edit-button" v-on:click="onToggleArchived()" :data-tooltip="archived == 1?'Move to main list':'Move to Archive'" data-position="bottom center" data-inverted>
|
||||
<span v-if="archived == 1"><i class="green archive icon"></i></span>
|
||||
<span v-if="archived != 1"><i class="archive icon"></i></span>
|
||||
</div>
|
||||
|
||||
<div class="edit-button" v-on:click="onTogglePinned" :data-tooltip="pinned == 1?'Un-pin from top':'Pin to top'" data-position="bottom center" data-inverted>
|
||||
<span v-if="pinned == 1"><i class="green pin icon"></i></span>
|
||||
<span v-if="pinned != 1"><i class="pin icon"></i></span>
|
||||
</div> -->
|
||||
|
||||
|
||||
<div class="edit-button" v-if="usersOnNote > 1">
|
||||
<div class="edit-button" v-if="usersOnNote > 1" :data-tooltip="`Viewers`" data-position="bottom center">
|
||||
<i class="green eye icon"></i> {{ usersOnNote }}
|
||||
</div>
|
||||
|
||||
@@ -123,7 +116,7 @@
|
||||
<i class="purple bolt icon"></i>
|
||||
</div> -->
|
||||
|
||||
<div class="edit-button" v-on:click=" hash=0; save() ">
|
||||
<div class="edit-button" v-on:click=" hash=0; save() " :data-tooltip="`Save\n(CTRL + S)`" data-position="bottom center">
|
||||
<i class="icons">
|
||||
<i class="grey save outline icon"></i>
|
||||
<i v-if="statusText == 'saved'" class="green small bottom left corner check icon"></i>
|
||||
@@ -131,7 +124,7 @@
|
||||
</i>
|
||||
</div>
|
||||
|
||||
<div class="edit-button" v-if="diffsApplied > 0">
|
||||
<div class="edit-button" v-if="diffsApplied > 0" :data-tooltip="`Unsaved Changes`" data-position="bottom center">
|
||||
+{{ diffsApplied }}
|
||||
</div>
|
||||
|
||||
@@ -172,8 +165,8 @@
|
||||
|
||||
</div>
|
||||
|
||||
<!-- little tags on the side -->
|
||||
<div class="note-mini-tag-area" :class="{ 'size-down':sizeDown }">
|
||||
<!-- little tags on the side, only show on desktop -->
|
||||
<div class="note-mini-tag-area" :class="{ 'size-down':sizeDown }" v-if="!$store.getters.getIsUserOnMobile">
|
||||
<span v-for="tag in allTags" class="subtle-tag active-mini-tag" v-if="isTagOnNote(tag.id)" v-on:click="removeTag(tag.id)">
|
||||
<i class="tag icon"></i>
|
||||
{{ tag.text }}
|
||||
@@ -222,7 +215,24 @@
|
||||
<div class="ui basic padded segment">
|
||||
<div class="ui grid">
|
||||
<div class="sixteen wide column">
|
||||
<h2>Additional Note Options</h2>
|
||||
<h3>Note Options</h3>
|
||||
</div>
|
||||
<div class="eight wide column">
|
||||
<div class="ui labeled icon fluid basic button" v-on:click="onToggleArchived()">
|
||||
<i class="archive icon" :class="{'green':(archived == 1)}"></i>
|
||||
<span v-if="archived == 1">Un-Archive Note</span>
|
||||
<span v-if="archived != 1">Archive Note</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="eight wide column">
|
||||
<div class="ui labeled icon fluid basic button" v-on:click="onTogglePinned">
|
||||
<i class="pin icon" :class="{'green':(pinned == 1)}"></i>
|
||||
<span v-if="pinned == 1">Un-Pin Note</span>
|
||||
<span v-if="pinned != 1">Pin Note</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sixteen wide column">
|
||||
<h3>List Options</h3>
|
||||
</div>
|
||||
<div class="sixteen wide column">
|
||||
<div class="ui labeled icon fluid basic button" v-on:click="sortList">
|
||||
@@ -242,6 +252,9 @@
|
||||
Uncheck All
|
||||
</div>
|
||||
</div>
|
||||
<div class="sixteen wide column">
|
||||
<h3>Misc Options</h3>
|
||||
</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>
|
||||
@@ -258,7 +271,7 @@
|
||||
</div>
|
||||
|
||||
<div class="sixteen wide column" v-if="rawTextId > 0">
|
||||
<h2>Share Note</h2>
|
||||
<h3>Share Note</h3>
|
||||
<share-note-component
|
||||
:note-id="noteid"
|
||||
:raw-text-id="rawTextId"
|
||||
@@ -287,7 +300,7 @@
|
||||
<div class="full-focus-shade shade1"
|
||||
:class="{ 'slide-out-left':sizeDown }"
|
||||
v-on:click="closeButtonAction()"></div>
|
||||
<div class="full-focus-shade shade2"
|
||||
<div class="full-focus-shade shade2"
|
||||
:class="{ 'slide-out-right':sizeDown }"
|
||||
v-on:click="closeButtonAction()"></div>
|
||||
|
||||
@@ -404,6 +417,7 @@
|
||||
}
|
||||
},
|
||||
beforeMount(){
|
||||
|
||||
this.$bus.$on('new_file_upload', ({noteId, imageCode}) => {
|
||||
if(this.noteid == noteId && this.editor){
|
||||
this.editor.moveCursorToEnd()
|
||||
@@ -438,6 +452,9 @@
|
||||
// document.addEventListener('visibilitychange', this.checkForUpdatedNote)
|
||||
|
||||
//Init squire as early as possible
|
||||
if(this.editor && this.editor.destroy){
|
||||
this.editor.destroy()
|
||||
}
|
||||
this.editor = new Squire( this.$refs.squirebox, {blockTag: 'p' })
|
||||
|
||||
this.$nextTick(() => {
|
||||
@@ -1052,10 +1069,11 @@
|
||||
titleResize(){
|
||||
//Resize the title field
|
||||
let element = this.$refs.titleTextarea
|
||||
let padding = 0
|
||||
if(element){
|
||||
|
||||
element.style.height = 'auto'
|
||||
element.style.height = (element.scrollHeight + padding) +'px'
|
||||
element.style.height = 'auto'
|
||||
element.style.height = (element.scrollHeight) +'px'
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1072,7 +1090,7 @@
|
||||
background-color: var(--menu-accent);
|
||||
z-index: 999;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
opacity: 0.88;
|
||||
}
|
||||
.shade1 {
|
||||
left: 50%;
|
||||
@@ -1089,7 +1107,8 @@
|
||||
left: 15%;
|
||||
right: 15%;
|
||||
z-index: 1005;
|
||||
overflow-x: scroll;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
scrollbar-width: none;
|
||||
scrollbar-color: transparent transparent;
|
||||
}
|
||||
@@ -1248,20 +1267,20 @@
|
||||
}
|
||||
/*End Settings manager styles */
|
||||
|
||||
|
||||
|
||||
/* container styles change based on mobile and number of open screens */
|
||||
.master-note-edit {
|
||||
/* .master-note-edit {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
height: 100vh;
|
||||
z-index: 1001;
|
||||
left: 15%;
|
||||
right: 15%;
|
||||
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
scrollbar-width: none;
|
||||
scrollbar-color: transparent transparent;
|
||||
}
|
||||
}*/
|
||||
.loading-note {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@@ -1282,19 +1301,18 @@
|
||||
}
|
||||
|
||||
/* One note open, in the middle of the screen */
|
||||
.master-note-edit.position-0 {
|
||||
left: 50%;
|
||||
right: 0;
|
||||
}
|
||||
.master-note-edit.full-focus {
|
||||
left: 15%;
|
||||
right: 15%;
|
||||
}
|
||||
.side-menu-open {
|
||||
left: calc(50% + 10px) !important;
|
||||
right: calc(0% + 10px) !important;
|
||||
}
|
||||
@media only screen and (max-width: 740px) {
|
||||
|
||||
.master-note-edit {
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
|
||||
.input-container-wrapper {
|
||||
left: 0;
|
||||
right: 0;
|
||||
@@ -1338,27 +1356,6 @@
|
||||
|
||||
|
||||
|
||||
/* Two Notes Open, each takes up 50% of the space */
|
||||
.master-note-edit.position-1 {
|
||||
left: 50%;
|
||||
right: 0%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.master-note-edit.position-2 {
|
||||
left: 0%;
|
||||
right: 50%;
|
||||
}
|
||||
.master-note-edit.position-3 {
|
||||
display: inline-block;
|
||||
position: inherit;
|
||||
width: 100%;
|
||||
min-height: 200px;
|
||||
height: auto;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* animations START */
|
||||
|
||||
.slide-out-top {
|
||||
|
||||
@@ -8,17 +8,6 @@
|
||||
<!-- Show title and snippet below it -->
|
||||
<div class="overflow-hidden note-card-text" @click="cardClicked" v-if="!titleView">
|
||||
|
||||
<span class="subtext" v-if="note.shareUsername">
|
||||
Shared by {{ note.shareUsername }}
|
||||
|
||||
<span v-if="note.opened == null && !beenClicked" class="ui tiny green compact right floated button">
|
||||
New
|
||||
</span>
|
||||
<span v-else-if="note.updated > note.opened && !beenClicked" class="ui tiny green compact right floated basic button">
|
||||
Updated
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span v-if="note.title == '' && note.subtext == ''">
|
||||
Empty Note
|
||||
</span>
|
||||
@@ -42,9 +31,22 @@
|
||||
Locked
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Shared Details -->
|
||||
<span class="subtext" v-if="note.shared == 2">
|
||||
You Shared this note
|
||||
<span v-if="note.updated > note.opened && !beenClicked" class="ui tiny green compact right floated basic button">
|
||||
<i class="green paper plane outline icon"></i> Shared
|
||||
<span v-if="note.updated/1000 > note.opened && !beenClicked" class="ui tiny green compact right floated basic button">
|
||||
Updated
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span class="subtext" v-if="note.shareUsername">
|
||||
<i class="green paper plane outline icon"></i> Shared by {{ note.shareUsername }}
|
||||
|
||||
<span v-if="note.opened == null && !beenClicked" class="ui tiny green compact right floated button">
|
||||
New
|
||||
</span>
|
||||
<span v-else-if="note.updated/1000 > note.opened && !beenClicked" class="ui tiny green compact right floated basic button">
|
||||
Updated
|
||||
</span>
|
||||
</span>
|
||||
@@ -235,11 +237,11 @@
|
||||
justClosed(){
|
||||
|
||||
// Scroll note into view
|
||||
this.$el.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'center',
|
||||
inline: 'center'
|
||||
})
|
||||
// this.$el.scrollIntoView({
|
||||
// behavior: 'smooth',
|
||||
// block: 'center',
|
||||
// inline: 'center'
|
||||
// })
|
||||
|
||||
//After scroll, trigger green outline animation
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
}
|
||||
|
||||
.img-row {
|
||||
height: 30vh;
|
||||
height: 20vh;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
.colors {
|
||||
position: absolute;
|
||||
z-index: 1023;
|
||||
top: 42px;
|
||||
top: 5px;
|
||||
/*height: 100px;*/
|
||||
/*width: 415px;*/
|
||||
left: 0;
|
||||
width: 400px;
|
||||
left: 20%;
|
||||
}
|
||||
.colors-container {
|
||||
max-width: 370px;
|
||||
|
||||
@@ -9,6 +9,8 @@ const SquireButtonFunctions = {
|
||||
activeList: false,
|
||||
activeToDo: false,
|
||||
activeColor: null,
|
||||
//
|
||||
lastUsedColor: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -49,7 +51,10 @@ const SquireButtonFunctions = {
|
||||
if(colorIndex > -1){
|
||||
//Get all digigs after color index, then limit to 3
|
||||
let colors = e.path.substring(colorIndex).match(/\d+/g).slice(0,3)
|
||||
this.activeColor=`rgb(${colors.join(',')})`
|
||||
|
||||
const lastColor = `rgb(${colors.join(',')})`
|
||||
this.activeColor = lastColor
|
||||
this.lastUsedColor = lastColor
|
||||
}
|
||||
},
|
||||
|
||||
@@ -93,6 +98,11 @@ const SquireButtonFunctions = {
|
||||
this.selectLineIfNoSelect()
|
||||
//Set color of font
|
||||
this.editor.setTextColour(color)
|
||||
|
||||
this.lastUsedColor = color
|
||||
},
|
||||
applyLastUsedColor(){
|
||||
this.modifyColor(this.lastUsedColor)
|
||||
},
|
||||
toggleList(type){
|
||||
|
||||
|
||||
@@ -1,43 +1,18 @@
|
||||
<template>
|
||||
<div class="ui grid">
|
||||
<div class="sixteen wide column"></div>
|
||||
<div class="sixteen wide column">
|
||||
<div class="text-container squire-box">
|
||||
|
||||
<h2 class="ui dividing header">
|
||||
Help
|
||||
</h2>
|
||||
<div class="ui segment squire-box">
|
||||
<h3 class="ui dividing header">
|
||||
<i class="green question circle icon"></i>
|
||||
Help
|
||||
</h3>
|
||||
|
||||
<p><u>Encrypted Data</u><br></p><blockquote><p>Note text, note title and the note search index is fully encrypted. <br></p><p><br></p><p>Encryption is the transformation of data into a form unreadable by anyone without the password. Its purpose is to ensure privacy by keeping the information hidden from anyone for whom it is not intended, even those who can see the encrypted data. If every note in the database was leaked, nothing would be readable. If the government asked for your notes, it would all be gibberish. <br></p></blockquote><p><br></p><p><u>Some Data is not encrypted</u><br></p><blockquote><p>To keep up ease of use, your Username, Files, Tags, Attachments and attachment search text is not encrypted.<br></p></blockquote><p><br></p><p><u>Searching</u><br></p><blockquote><p>Note text searching is limited to key words.<br></p><p>To allow for higher security, the complexity of the search index has been reduced. To make sure that every word of every note is fully encrypted, the search index is encrypted as well. <br></p></blockquote><p><br></p><p><u>Files</u><br></p><blockquote><p>The files section will show files that have been uploaded to notes or web pages that have been scraped from notes.<br></p><p><br></p><p>The text next to each file can be modified and found later with the search bar. <br></p><p>Files are limited to 5MB in size. <br></p><p><br></p><p>Files can be uploaded to notes. If its an image, the picture will be put into the note.<br></p><p>Images added to notes will have text scanned, so it can be searched (This isn't super accurate so don't rely to heavily on it.) The text can be updated at any time and searched later.<br></p></blockquote><p><br></p><p><u>Scratch Pad</u><br></p><blockquote><p>The Scratch Pad was designed to allow rapid input to a single note. Rather than junking up all your notes with random links, numbers or haikus, you can put them all in one place. <br></p></blockquote><p><br></p><p><u>Flux Theme</u><br></p><blockquote><p>Flux theme limits the amount of blue emitted by your screen. Most things turn sepia and a filter is applied to images to make them more sepia. Less blue light at night is supposed to be helpful for falling asleep.<br></p><p>Here is some good research on the topic: <a href="https://justgetflux.com/research.html">https://justgetflux.com/research.html</a><br></p></blockquote><p><br></p><p><u>Text Editor Keyboard Shortcuts</u><br></p><blockquote><ul><li><b>Number List</b> - CTRL + SHIFT + 9<br></li><li><b>Todo List</b> - CTRL + SHIFT + 8<br></li><li><b>Underline</b> - CTRL + u<br></li><li><b>Bold</b> - CTRL + b<br></li><li><b>Quote</b> - CRTL + i<br></li><li><b>Undo</b> - CTRL + z<br></li><li><b>Redo</b> - CTRL + y<br></li><li><b>Indent</b> - TAB<br></li><li><b>Un-Indent</b> - SHIFT + TAB<br></li><li><b>Manual Save</b> - CTRL + S<br></li><li><b>Close Note</b> - ESC<br></li></ul></blockquote><p><br></p><p><u>Shared Notes</u><br></p><blockquote><p>Shared notes still respect privacy but use a different security scheme. Instead of encrypting the note with your password, a shared password is created. This note then uses <a href="https://en.wikipedia.org/wiki/Public-key_cryptography">public-key cryptography</a> to share the note with other users, while still making it unreadable to anyone else. <br></p><ul><li>You can revoke any users access to a shared note at any time.<br></li><li>Notes you share can not be shared by other people.<br></li><li>If you turn off sharing, all users lose access and the note is reverted to the default security scheme.<br></li></ul><p><br></p><p>It is possible to generate a public URL for shared notes. This exposes the Encryption Key for the note. Anyone with the key to the note can read that note. The encryption key to a shared note can easily be changed by turning off sharing, then turning it back on. <br></p><p><u><br></u></p></blockquote><p><u>Links in notes</u><br></p><blockquote><p>Links put into notes are automatically scraped. This means the data from the link will be scanned to get an image and some text from the website to help make that link more accessible in the future. <br></p><p>You can edit the text of scarped links and any time and search for it later. <br></p></blockquote><p><br></p><p><u>Deleting Notes</u><br></p><blockquote><p>Deleting a note from the trash erases all note text and the note entry. It can not be recovered.<br></p><p>None of the files related to the note are deleted. They can be manually removed at any time.<br></p></blockquote><p><br></p><p><u>Daily Backups</u><br></p><blockquote><p>All notes are backed up, every night, at midnight. If there is data loss, it can be restored from a backup. If you experience some sort of cataclysmic data loss please contact the system administrator for a copy of your data or a restoration procedure. <br></p><p>All data in the backups remains encrypted and unreadable.<br></p></blockquote><p><br></p><p><u>Two Factor Authentication (2FA)</u><br></p><blockquote>Line truncated
|
||||
|
||||
<!-- Content copied from note -->
|
||||
<!-- https://www.solidscribe.com/#/notes/open/552 -->
|
||||
<p><b>Only Note Text is Encrypted</b><br></p><blockquote><p>Only you can read your notes. Encryption is the transformation of data into a form unreadable by anyone without the password. Its purpose is to ensure privacy by keeping the information hidden from anyone for whom it is not intended, even those who can see the encrypted data. Even if every note in the database was leaked, nothing would be readable. If the government asked for your notes, it would all be gibberish. <br></p></blockquote><p><br></p><p><b>Some Data is not encrypted</b><br></p><blockquote><p>Everything isn't encrypted, to keep up ease of use. Files, Tags and Attachments are not encrypted.<br></p></blockquote><p><br></p><p><b>Searching is somewhat limited</b><br></p><blockquote><p>Since every note is encrypted, searching is limited. To maintain security, only single words can be searched. Your search index is private and Encrypted.<br></p></blockquote><p><br></p><p><b>The Scrat</b><b></b><b>ch Pad</b><b></b><br></p><blockquote><p>The Scratch Pad was designed to allow rapid input to a single note. Rather than junking up all your notes with random links, numbers or haikus, you can put them all in one place. <br></p><ul><li>All data pushed to the quick note can still be edited like a normal note.<br></li><li>Creating a new quick note will not erase your old <br></li></ul></blockquote><p><br></p><p><b>Flux Theme</b><br></p><blockquote><p>Flux theme limits the amount of blue emitted by your screen. Most things turn sepia and a filter is applied to images to make them more sepia. Less blue light at night is supposed to be helpful for falling asleep.<br></p><p>Here is some good research on the topic: <a href="https://justgetflux.com/research.html">https://justgetflux.com/research.html</a><br></p></blockquote><p><br></p><p><b>Text Editor Keyboard Shor</b><b>tcuts</b><br></p><blockquote><p>Number List - CTRL + SHIFT + 9<br></p><p>Todo List - CTRL + SHIFT + 8<br></p><p>Underline CTRL + u<br></p><p>Bold - CTRL + b<br></p><p>Quote - CRTL + i<br></p><p>Indent - CTL + ]<br></p><p>Outdent - CRTL + [<br></p><p>Undo - CTRL + z<br></p><p>Redo - CTRL + y<br></p></blockquote><p><br></p><p><b>Shared Notes and Security</b><br></p><blockquote><p>Shared notes still respect privacy but use a different security scheme. Instead of encrypting the note with your password, a shared password is created. This note then uses <a href="https://en.wikipedia.org/wiki/Public-key_cryptography">public-key cryptography</a> to share the note with other users, while still making it unreadable to anyone else. <br></p><ul><li>You can revoke any users access to a shared note at any time.<br></li><li>Notes you share can not be shared by other people.<br></li><li>If you turn off sharing, all users lose access and the note is reverted to the default security scheme.<br></li></ul></blockquote><p><b>Public Links for shared notes</b><br></p><blockquote><p>It is possible to generate a public URL for shared notes. This is a huge security risk and exposes the Encryption Key for the note. The encryption key to a shared note can easily be changed by turning off sharing, then turning it back on. <br></p><p><br></p></blockquote><p><b>Links in notes</b><br></p><blockquote><p>Links put into notes are automatically scraped. This means the data from the link will be scanned to get an image and some text from the website to help make that link more accessible in the future. You can edit the text of scarped links and any time and search for it later. <br></p></blockquote><p><br></p><p><b>Files in notes</b><br></p><blockquote><p>Files can be uploaded to notes. If its an image, the picture will be put into the note.<br></p><p>Images added to notes will have text scanned, so it can be searched (This isn't super accurate so don't rely to heavily on it.) The text can be updated at any time.<br></p></blockquote><p><br></p><p><b>Deleting notes</b><br></p><blockquote><p>When<b> </b>notes are deleted, none of the files related to the note are deleted. <br></p></blockquote><p><br></p><p><b>Daily Backups</b><br></p><blockquote><p>All notes are backed up, every night, at midnight. If there is data loss, it can be restored from a backup. If you experience some sort of cataclysmic data loss please contact the system administrator for a copy of your data or a restoration procedure.<br></p></blockquote>
|
||||
|
||||
</div>
|
||||
|
||||
<h2 class="ui dividing header">
|
||||
Solid Scribe Terms of Use
|
||||
</h2>
|
||||
<div class="ui segment">
|
||||
|
||||
<p>Welcome to Solid Scribe’s web-based application at solidscribe.com.<br></p><p><br></p><p>Your use of the Site and/or participation in any of the Services offered through the Site, is subject to these Terms of Use.<br></p><p>By registering an an account or using the Site you agree to be bound by these Terms of Use.<br></p><p><br></p><p>The Company reserves the right to change these Terms of Use from time to time by publishing them on the Site.<br></p><p>By using the Site, you agree to the most current version of these Terms of Use as posted on the Site.<br></p><p><br></p><p>All content on the Site is provided "as is." Solid Scribe is not liable for any informational errors, incompleteness, delays or any actions taken in reliance on information contained on the Site. The Company cannot and does not guarantee that any content on the Site will be entirely accurate or up-to-date at all times as the information on the Site originates from various sources, which may or may not be within the control of the Company.<br></p><p><br></p><p>The Company is not responsible for any omission, deletion, failure to store, improper delivery or untimely delivery of any information or material. The Company is not responsible for any reliance upon or damage arising from the use of the Site, including technical damage resulting from downloading or accessing any information or material provided.<br></p><p><br></p><p>You understand and agree that the Company is not responsible for any content posted by you or third parties. You further understand that the Company does not monitor the content posted by other users.<br></p><p><br></p><p>The Company reserves the right to remove content that the Company deems inappropriate for any reason what so ever without notice. The Company also reserves the right, in the Company’s sole discretion, to remove a user's privilege to post content on the Site.<br></p><p><br></p><p>The Company does not in any manner endorse the content of its users and cannot and does not vouch for its reliability or otherwise accept liability for it.<br></p><p><br></p><p>Minors (persons under 18 years of age), persons or organizations that cannot enter into binding contracts under applicable law, and those who may be prevented by contractual obligations from entering into binding contracts with Solid Scribe are not allowed to use this Site.<br></p><p><br></p><p>The Company has the right, but not the obligation, to take any of the following actions in the Company’s sole discretion at any time and for any reason without providing you any prior notice:<br></p><p><br></p><p>1. Restrict, suspend, modify or terminate access to the Site or all or any part of the Company’s Services;<br></p><p><br></p><p>2. Change, suspend or discontinue all or any part of the Company’s Services;<br></p><p><br></p><p>3. Refuse, move, modify or remove any material submitted to the Site by any person, for any reason;<br></p><p><br></p><p>4. Refuse, move, modify or remove any content available on the Site;<br></p><p><br></p><p>5. Deactivate or delete your account and all related information and files in your account;<br></p><p><br></p><p>6. Establish, discontinue or change general practices and limits concerning use of the Site and the Company’s Services.<br></p><p><br></p><p>You agree that the Company is not liable to you or any third party for taking any of these actions. Any registration or other eligibility requirements are in the sole discretion of the Company and may be modified for any reason at any time.<br></p><p><br></p><p>YOUR INFORMATION AND CONTENT<br></p><p><br></p><p>Any information you provide to Solid Scribe or other users during your registration or use of the Site (in any public message area, through any mail feature, or otherwise generated out of your use of the Site or the Services), is subject to the following:<br></p><p><br></p><p>(a) You are solely responsible for the content of your information, postings or Notes. The Company reserves the right to take any action with respect to such information that it deems necessary or appropriate in its sole discretion for any reason, including if the Company believes information is inappropriate for the Site or the Services, may create liability for the Company.<br></p><p><br></p><p>(b) You agree that your information will not, to the best of your knowledge: (i) violate any law (including without limitation those governing export control, consumer protection, unfair competition, anti-discrimination or false advertising); or (ii) be libelous, defamatory, obscene, abusive, violate a third party's right to privacy, otherwise violate any applicable local, state, national or international law, amount to spamming or be otherwise inappropriate. You may not post content that degrades others on the basis of gender, race, class, ethnicity, national origin, religion, sexual preference, disability or other classification. Epithets and other language intended to intimidate, unlawLine truncated
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'HelpPage',
|
||||
props:[ 'message' ],
|
||||
data () {
|
||||
return {
|
||||
items: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
export default {
|
||||
name: 'HelpPage',
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -19,7 +19,7 @@
|
||||
v-if="$store.getters.totals && ($store.getters.totals['youGotMailCount'] > 0)"
|
||||
style="position: relative;">
|
||||
<i class="green mail icon"></i>Inbox
|
||||
+{{ $store.getters.totals['youGotMailCount'] }}
|
||||
<span class="tiny circular floating ui green label">+{{ $store.getters.totals['youGotMailCount'] }}</span>
|
||||
</div>
|
||||
|
||||
<tag-display
|
||||
|
||||
+154
-121
@@ -1,129 +1,162 @@
|
||||
<template>
|
||||
<div class="ui grid">
|
||||
<div class="row"></div>
|
||||
<!-- spacer column -->
|
||||
<div class="sixteen wide column">
|
||||
<h2 class="ui dividing header">
|
||||
<i class="cog icon"></i>
|
||||
Settings
|
||||
</h2>
|
||||
<div class="squire-box">
|
||||
<div class="">
|
||||
|
||||
<div class="ui segment">
|
||||
<h3>Change Password</h3>
|
||||
<p>Create a new scratch pad. Old scratch pad will turn into a normal note.</p>
|
||||
<div class="ui compact basic button shrinking" v-if="!showNewNoteConfirm" v-on:click="showNewNoteConfirm = true">
|
||||
<i class="sync alternate reload icon"></i>
|
||||
New Scratch Pad
|
||||
</div>
|
||||
<div v-if="showNewNoteConfirm" class="ui compact basic button shrinking" v-on:click="showNewNoteConfirm = false">
|
||||
<i class="close icon"></i>
|
||||
Cancel
|
||||
</div>
|
||||
<div v-if="showNewNoteConfirm" class="ui compact basic button shrinking" v-on:click="newQuickNote()">
|
||||
<i class="green thumbs up icon"></i>
|
||||
Confirm
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="ui dividing header">
|
||||
<i class="green cog icon"></i>
|
||||
Settings
|
||||
</h3>
|
||||
|
||||
<!-- Accent Color -->
|
||||
<div class="ui segment">
|
||||
<div class="ui grid">
|
||||
<div class="sixteen wide column">
|
||||
<h3 class="ui header">
|
||||
Accent Color
|
||||
</h3>
|
||||
<div
|
||||
v-for="color in themeColors"
|
||||
class="ui compact basic button"
|
||||
:style="`background: linear-gradient(0deg, ${color} 4%, rgba(0,0,0,0) 5%);`"
|
||||
v-on:click="setAccentColor(color)">
|
||||
<logo style="width: 33px; height: auto;" :color="color" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Enable Two Factor -->
|
||||
<div class="ui segment">
|
||||
<h3>Two Factor Authentication</h3>
|
||||
<div class="ui stackable grid">
|
||||
<div class="four wide column">
|
||||
<p>1. Enter Password and get QR</p>
|
||||
<div class="ui fluid action input">
|
||||
<input type="password" placeholder="Current Password" v-model="password">
|
||||
|
||||
<div v-if="password.length == 0" class="ui disabled button">
|
||||
Get QR code
|
||||
</div>
|
||||
<div v-if="password.length > 0" class="ui green button" v-on:click="getQrCode()">
|
||||
Get QR code
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="five wide column">
|
||||
<p>2. Scan QR Code</p>
|
||||
<p v-if="qrCode == ''">QR Code Will appear here.</p>
|
||||
<img v-if="qrCode != ''" :src="qrCode" alt="QR Code">
|
||||
</div>
|
||||
<div class="four wide column">
|
||||
<p>3. Verify with code</p>
|
||||
<div class="ui input" v-if="qrCode != ''">
|
||||
<input type="text" placeholder="Verification Code" v-model="verificationToken" v-on:keyup.enter="verifyQrCode()">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- change password -->
|
||||
<div class="ui segment">
|
||||
<h3>Change Password</h3>
|
||||
<div class="ui grid">
|
||||
<div class="five wide column">
|
||||
<label>Current Password</label>
|
||||
<div class="ui fluid input">
|
||||
<input v-model="change1" type="password" placeholder="Current Password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="five wide column">
|
||||
<label>New Password</label>
|
||||
<div class="ui fluid input">
|
||||
<input v-model="change2" type="password" placeholder="New Password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="six wide column">
|
||||
<label>Rereat New Password</label>
|
||||
<div class="ui fluid action input">
|
||||
<input v-model="change3" type="password" placeholder="Repeat Password">
|
||||
<div v-on:click="passwordChange()" class="ui button" :class="{'green':(change1.length > 0 && change2 == change3)}">
|
||||
Change it!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- log out -->
|
||||
<div class="ui segment">
|
||||
<div class="ui grid">
|
||||
<div class="sixteen wide column">
|
||||
<h3>Log Out</h3>
|
||||
</div>
|
||||
<div class="eight wide column">
|
||||
<div class="ui button" v-on:click="logout()">
|
||||
Log Out this browser
|
||||
</div>
|
||||
</div>
|
||||
<div class="eight wide column">
|
||||
<div class="ui button" v-on:click="revokeAllSessions()">
|
||||
Log Out all other browsers
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4>New Scratch Pad</h4>
|
||||
<div class="ui segment">
|
||||
<p>Create a new scratch pad. Old scratch pad will turn into a normal note.</p>
|
||||
<div class="ui compact basic button shrinking" v-if="!showNewNoteConfirm" v-on:click="showNewNoteConfirm = true">
|
||||
<i class="sync alternate reload icon"></i>
|
||||
New Scratch Pad
|
||||
</div>
|
||||
<div v-if="showNewNoteConfirm" class="ui compact basic button shrinking" v-on:click="showNewNoteConfirm = false">
|
||||
<i class="close icon"></i>
|
||||
Cancel
|
||||
</div>
|
||||
<div v-if="showNewNoteConfirm" class="ui compact basic button shrinking" v-on:click="newQuickNote()">
|
||||
<i class="green thumbs up icon"></i>
|
||||
Confirm
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Accent Color -->
|
||||
<h4 class="ui header">
|
||||
Accent Color
|
||||
</h4>
|
||||
<div class="ui segment">
|
||||
<div class="ui doubling grid">
|
||||
<div class="sixteen wide column">
|
||||
<p>Theme changes are only saved to this browser.</p>
|
||||
<div
|
||||
v-for="color in themeColors"
|
||||
class="ui compact basic button"
|
||||
:style="`background: linear-gradient(0deg, ${color} 4%, rgba(0,0,0,0) 5%);`"
|
||||
v-on:click="setAccentColor(color)">
|
||||
<logo style="width: 33px; height: auto;" :color="color" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Enable Two Factor -->
|
||||
<h4>Two Factor Authentication</h4>
|
||||
<div class="ui segment">
|
||||
<div class="ui stackable grid">
|
||||
<div class="six wide column">
|
||||
<p>1. Enter Password and get QR</p>
|
||||
<div class="ui fluid action input">
|
||||
<input type="password" placeholder="Current Password" v-model="password">
|
||||
|
||||
<div v-if="password.length == 0" class="ui disabled button">
|
||||
Get QR code
|
||||
</div>
|
||||
<div v-if="password.length > 0" class="ui green button" v-on:click="getQrCode()">
|
||||
Get QR code
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="four wide column">
|
||||
<p>2. Scan QR Code</p>
|
||||
<p v-if="qrCode == ''">(QR Code will appear here)</p>
|
||||
<img v-if="qrCode != ''" :src="qrCode" alt="QR Code">
|
||||
</div>
|
||||
<div class="six wide column">
|
||||
<p>3. Verify with code</p>
|
||||
<div class="ui fluid action input" v-if="qrCode != ''">
|
||||
<input type="text" placeholder="Verification Code" v-model="verificationToken" v-on:keyup.enter="verifyQrCode()">
|
||||
<div class="ui green button">
|
||||
Verify!
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui fluid action input" v-if="qrCode == ''">
|
||||
<input type="text" placeholder="Verification Code" >
|
||||
<div class="ui disabled button">
|
||||
Verify!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- change password -->
|
||||
<h4>Change Password</h4>
|
||||
<div class="ui segment">
|
||||
<div class="ui stackable grid">
|
||||
<div class="five wide column">
|
||||
<p>Current Password</p>
|
||||
<div class="ui fluid input">
|
||||
<input v-model="change1" type="password" placeholder="Current Password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="five wide column">
|
||||
<p>New Password</p>
|
||||
<div class="ui fluid input">
|
||||
<input v-model="change2" type="password" placeholder="New Password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="six wide column">
|
||||
<p>Rereat New Password</p>
|
||||
<div class="ui fluid action input">
|
||||
<input v-model="change3" type="password" placeholder="Repeat Password">
|
||||
<div v-on:click="passwordChange()" class="ui button" :class="{'green':(change1.length > 0 && change2 == change3)}">
|
||||
Change it!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- log out -->
|
||||
<h4>Log Out</h4>
|
||||
<div class="ui segment">
|
||||
<div class="ui stackable grid">
|
||||
<div class="eight wide column">
|
||||
<div class="ui button" v-on:click="logout()">
|
||||
<i class="power off icon"></i>
|
||||
Log Out on this browser
|
||||
</div>
|
||||
</div>
|
||||
<div class="eight wide column">
|
||||
<div class="ui button" v-on:click="revokeAllSessions()">
|
||||
<i class="sign out icon"></i>
|
||||
Log Out all other browsers
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4>Export All Data (In Development)</h4>
|
||||
<div class="ui segment">
|
||||
<p>Download all files and notes in raw text or html</p>
|
||||
<div class="ui button">Export all Data</div>
|
||||
</div>
|
||||
|
||||
<h4>Delete Account (In Development)</h4>
|
||||
<div class="ui segment">
|
||||
<div class="ui stackable grid">
|
||||
<div class="eight wide column">
|
||||
<p>Delete all data. This can not be undone.</p>
|
||||
<div class="ui fluid input">
|
||||
<input type="password" placeholder="Current Password" v-model="password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="four wide bottom aligned column">
|
||||
<div class="ui fluid button">Verify</div>
|
||||
</div>
|
||||
<div class="four wide bottom aligned column">
|
||||
<div class="ui disabled fluid button">Delete Account</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<div class="text-container squire-box">
|
||||
|
||||
<h3 class="ui dividing header">
|
||||
<i class="green info circle icon"></i>
|
||||
Solid Scribe Terms of Use
|
||||
</h3>
|
||||
|
||||
<p><span class="size" style="font-size: 1.4em">Solid Scribe Terms of Use</span><br></p><p><br></p><p>Welcome to Solid Scribe’s web-based application at solidscribe.com.<br></p><p><br></p><p>Your use of the Site and/or participation in any of the Services offered through the Site, is subject to these Terms of Use.<br></p><p>By registering an account or using the Site you agree to be bound by these Terms of Use.<br></p><p><br></p><p>The Company reserves the right to change these Terms of Use from time to time by publishing them on the Site.<br></p><p>By using the Site, you agree to the most current version of these Terms of Use as posted on the Site.<br></p><p><br></p><p>All content on the Site is provided "as is." Solid Scribe is not liable for any informational errors, incompleteness, delays or any actions taken in reliance on information contained on the Site. The Company cannot and does not guarantee that any content on the Site will be entirely accurate or up-to-date at all times as the information on the Site originates from various sources, which may or may not be within the control of the Company.<br></p><p><br></p><p>The Company is not responsible for any omission, deletion, failure to store, improper delivery or untimely delivery of any information or material. The Company is not responsible for any reliance upon or damage arising from the use of the Site, including technical damage resulting from downloading or accessing any information or material provided.<br></p><p><br></p><p>You understand and agree that the Company is not responsible for any content posted by you or third parties. You further understand that the Company does not monitor the content posted by other users.<br></p><p><br></p><p>The Company reserves the right to remove content that the Company deems inappropriate for any reason what so ever without notice. The Company also reserves the right, in the Company’s sole discretion, to remove a user's privilege to post content on the Site.<br></p><p><br></p><p>The Company does not in any manner endorse the content of its users and cannot and does not vouch for its reliability or otherwise accept liability for it.<br></p><p><br></p><p>Minors (persons under 18 years of age), persons or organizations that cannot enter into binding contracts under applicable law, and those who may be prevented by contractual obligations from entering into binding contracts with Solid Scribe are not allowed to use this Site.<br></p><p><br></p><p>The Company has the right, but not the obligation, to take any of the following actions in the Company’s sole discretion at any time and for any reason without providing you any prior notice:<br></p><p><br></p><p>1. Restrict, suspend, modify or terminate access to the Site or all or any part of the Company’s Services;<br></p><p>2. Change, suspend or discontinue all or any part of the Company’s Services;<br></p><p>3. Refuse, move, modify or remove any material submitted to the Site by any person, for any reason;<br></p><p>4. Refuse, move, modify or remove any content available on the Site;<br></p><p>5. Deactivate or delete your account and all related information and files in your account;<br></p><p>6. Establish, discontinue or change general practices and limits concerning use of the Site and the Company’s Services.<br></p><p><br></p><p>You agree that the Company is not liable to you or any third party for taking any of these actions. Any registration or other eligibility requirements are in the sole discretion of the Company and may be modified for any reason at any time.<br></p><p><br></p><p>YOUR INFORMATION AND CONTENT<br></p><p><br></p><p>Any information you provide to Solid Scribe or other users during your registration or use of the Site (in any public message area, through any mail feature, or otherwise generated out of your use of the Site or the Services), is subject to the following:<br></p><p><br></p><p>(a) You are solely responsible for the content of your information, postings or Notes. The Company reserves the right to take any action with respect to such information that it deems necessary or appropriate in its sole discretion for any reason, including if the Company believes information is inappropriate for the Site or the Services, may create liability for the Company.<br></p><p><br></p><p>(b) You agree that your information will not, to the best of your knowledge: (i) violate any law (including without limitation those governing export control, consumer protection, unfair competition, anti-discrimination or false advertising); or (ii) be libelous, defamatory, obscene, abusive, violate a third party's right to privacy, otherwise violate any applicable local, state, national or international law, amount to spamming or be otherwise inappropriate. You may not post content that degrades others on the basis of gender, race, class, ethnicity, national origin, religion, sexual preference, disability or other classification. Epithets and otherLine truncated
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'TermsPage',
|
||||
}
|
||||
</script>
|
||||
@@ -6,6 +6,7 @@ import Router from 'vue-router'
|
||||
const HomePage = () => import(/* webpackChunkName: "HomePage" */ '@/pages/HomePage')
|
||||
const LoginPage = () => import(/* webpackChunkName: "LoginPage" */ '@/pages/LoginPage')
|
||||
const HelpPage = () => import(/* webpackChunkName: "HelpPage" */ '@/pages/HelpPage')
|
||||
const TermsPage = () => import(/* webpackChunkName: "TermsPage" */ '@/pages/TermsPage')
|
||||
const SettingsPage = () => import(/* webpackChunkName: "SettingsPage" */ '@/pages/SettingsPage')
|
||||
const SharePage = () => import(/* webpackChunkName: "SharePage" */ '@/pages/SharePage')
|
||||
const NotesPage = () => import(/* webpackChunkName: "NotesPage" */ '@/pages/NotesPage')
|
||||
@@ -53,7 +54,13 @@ export default new Router({
|
||||
meta: {title:'Help'},
|
||||
component: HelpPage
|
||||
},
|
||||
{
|
||||
{
|
||||
path: '/terms',
|
||||
name: 'Terms',
|
||||
meta: {title:'Terms'},
|
||||
component: TermsPage
|
||||
},
|
||||
{
|
||||
path: '/settings',
|
||||
name: 'Settings',
|
||||
meta: {title:'Settings'},
|
||||
|
||||
+21
-12
@@ -21,17 +21,17 @@ const port = 3000
|
||||
//
|
||||
// Request Rate Limiter
|
||||
//
|
||||
const rateLimit = require('express-rate-limit');
|
||||
const rateLimit = require('express-rate-limit')
|
||||
//Limiter for the entire app
|
||||
const limiter = rateLimit({
|
||||
windowMs: 10 * 60 * 1000, // minutes
|
||||
max: 1000 // limit each IP to 100 requests per windowMs
|
||||
});
|
||||
windowMs: 10 * 60 * 1000, // 10 minutes
|
||||
max: 1000 // limit each IP to 1000 requests per windowMs
|
||||
})
|
||||
|
||||
// apply to all requests
|
||||
app.use(limiter);
|
||||
|
||||
|
||||
|
||||
var http = require('http').createServer(app);
|
||||
var io = require('socket.io')(http, {
|
||||
path:'/socket'
|
||||
@@ -222,6 +222,7 @@ app.use(express.json({limit: '5mb'}))
|
||||
app.use(function(req, res, next){
|
||||
|
||||
//Always null out master key, never allow it set from outside
|
||||
req.headers.userId = null
|
||||
req.headers.masterKey = null
|
||||
req.headers.sessionId = null
|
||||
|
||||
@@ -243,10 +244,7 @@ app.use(function(req, res, next){
|
||||
})
|
||||
.catch(error => {
|
||||
|
||||
console.log(error)
|
||||
|
||||
res.statusMessage = error //Throw 400 error if token is bad
|
||||
res.status(400).end()
|
||||
next('Unauthorized')
|
||||
})
|
||||
} else {
|
||||
next() //No token. Move along.
|
||||
@@ -260,13 +258,12 @@ let UserTest = require('@models/User')
|
||||
let NoteTest = require('@models/Note')
|
||||
let AuthTest = require('@helpers/Auth')
|
||||
|
||||
Auth.testTwoFactor()
|
||||
|
||||
Auth.test()
|
||||
UserTest.keyPairTest('genMan23', '1', printResults)
|
||||
UserTest.keyPairTest('genMan25', '1', printResults)
|
||||
.then( ({testUserId, masterKey}) => NoteTest.test(testUserId, masterKey, printResults))
|
||||
.then( message => {
|
||||
if(printResults) console.log(message)
|
||||
Auth.testTwoFactor()
|
||||
})
|
||||
|
||||
|
||||
@@ -303,4 +300,16 @@ app.use('/api/quick-note', quickNote)
|
||||
//Output running status
|
||||
app.listen(port, () => {
|
||||
// console.log(`Listening on port ${port}!`)
|
||||
})
|
||||
|
||||
//
|
||||
//Error handlers
|
||||
//
|
||||
//Default error handler just say unauthorized for everything
|
||||
app.use(function (err, req, res, next) {
|
||||
if (err) {
|
||||
res.status(401).send('Unauthorized')
|
||||
return
|
||||
}
|
||||
next()
|
||||
})
|
||||
+15
-9
@@ -449,8 +449,7 @@ Note.update = (userId, noteId, noteText, noteTitle, color, pinned, archived, has
|
||||
//Shared notes use encrypted key - decrypt key then decrypt note
|
||||
const encryptedShareKey = rows[0][0].encrypted_share_password_key
|
||||
if(encryptedShareKey != null){
|
||||
masterKey = crypto.privateDecrypt(userPrivateKey,
|
||||
Buffer.from(encryptedShareKey, 'base64') )
|
||||
masterKey = crypto.privateDecrypt(userPrivateKey, Buffer.from(encryptedShareKey, 'base64') )
|
||||
}
|
||||
|
||||
let encryptedNoteText = ''
|
||||
@@ -475,10 +474,14 @@ Note.update = (userId, noteId, noteText, noteTitle, color, pinned, archived, has
|
||||
for (var i = 0; i < rows[0].length; i++) {
|
||||
const otherNote = rows[0][i]
|
||||
//Re-encrypt for other user
|
||||
const updatedSnippet = cs.encrypt(masterKey, otherNote.snippet_salt, snippet)
|
||||
let updatedSnippet = '' //Default to no snippet
|
||||
if(noteText.length > 500){
|
||||
updatedSnippet = cs.encrypt(masterKey, otherNote.snippet_salt, snippet)
|
||||
}
|
||||
db.promise().query('UPDATE note SET snippet = ? WHERE id = ?', [updatedSnippet, otherNote.id])
|
||||
|
||||
SocketIo.to(otherNote['user_id']).emit('new_note_text_saved', {'noteId':otherNote.id, hash})
|
||||
.then((rows, fields) => {
|
||||
SocketIo.to(otherNote['user_id']).emit('new_note_text_saved', {'noteId':otherNote.id, hash})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -489,10 +492,13 @@ Note.update = (userId, noteId, noteText, noteTitle, color, pinned, archived, has
|
||||
})
|
||||
.then( (rows, fields) => {
|
||||
|
||||
//Set openend time to a minute ago
|
||||
const theFuture = Math.round((+new Date)/1000) + 10
|
||||
|
||||
//Update other note attributes
|
||||
return db.promise()
|
||||
.query('UPDATE note SET pinned = ?, archived = ?, color = ?, snippet = ?, indexed = 0 WHERE id = ? AND user_id = ? LIMIT 1',
|
||||
[pinned, archived, color, noteSnippet, noteId, userId])
|
||||
.query('UPDATE note SET pinned = ?, archived = ?, color = ?, snippet = ?, indexed = 0, opened = ? WHERE id = ? AND user_id = ? LIMIT 1',
|
||||
[pinned, archived, color, noteSnippet, theFuture, noteId, userId])
|
||||
|
||||
})
|
||||
.then((rows, fields) => {
|
||||
@@ -750,7 +756,6 @@ Note.get = (userId, noteId, masterKey) => {
|
||||
})
|
||||
.then((rows, fields) => {
|
||||
|
||||
const nowTime = Math.round((+new Date)/1000)
|
||||
let noteLockedOut = false
|
||||
let noteData = rows[0][0]
|
||||
// const rawTextId = noteData['rawTextId']
|
||||
@@ -776,6 +781,7 @@ Note.get = (userId, noteId, masterKey) => {
|
||||
noteData.title = textObject[0]
|
||||
noteData.text = textObject[1]
|
||||
|
||||
const nowTime = Math.round((+new Date)/1000)
|
||||
db.promise().query(`UPDATE note SET opened = ? WHERE (id = ?)`, [nowTime, noteId])
|
||||
|
||||
//Return note data
|
||||
@@ -1122,7 +1128,7 @@ Note.search = (userId, searchQuery, searchTags, fastFilters, masterKey) => {
|
||||
}
|
||||
}
|
||||
} catch(err) {
|
||||
console.log('Error opening note id -> ', note.id)
|
||||
console.log('Error opening note id -> '+note.id+' for userId -> '+userId)
|
||||
console.log(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ const speakeasy = require('speakeasy')
|
||||
|
||||
let User = module.exports = {}
|
||||
|
||||
const version = '3.1.5'
|
||||
const version = '3.1.6'
|
||||
|
||||
//Login a user, if that user does not exist create them
|
||||
//Issues login token
|
||||
@@ -498,6 +498,7 @@ User.deleteUser = (userId, password) => {
|
||||
|
||||
let deletePromises = []
|
||||
|
||||
//Delete all notes and raw text
|
||||
let noteDelete = db.promise().query(`
|
||||
DELETE note, note_raw_text
|
||||
FROM note
|
||||
@@ -506,12 +507,14 @@ User.deleteUser = (userId, password) => {
|
||||
`,[userId])
|
||||
deletePromises.push(noteDelete)
|
||||
|
||||
//Delete user entry
|
||||
let userDelete = db.promise().query(`
|
||||
DELETE FROM user WHERE id = ?
|
||||
`,[userId])
|
||||
deletePromises.push(userDelete)
|
||||
|
||||
let tables = ['user_key', 'user_encrypted_search_index', 'attachment']
|
||||
//Delete user_key, encrypted search index
|
||||
let tables = ['user_key', 'user_encrypted_search_index']
|
||||
tables.forEach(tableName => {
|
||||
|
||||
const query = `DELETE FROM ${tableName} WHERE user_id = ?`
|
||||
@@ -519,6 +522,8 @@ User.deleteUser = (userId, password) => {
|
||||
deletePromises.push(deleteQuery)
|
||||
})
|
||||
|
||||
//Remove all note attachments and files
|
||||
|
||||
return Promise.all(deletePromises)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,16 +6,23 @@ let router = express.Router()
|
||||
|
||||
let Attachment = require('@models/Attachment')
|
||||
let Note = require('@models/Note')
|
||||
|
||||
let userId = null
|
||||
let masterKey = null
|
||||
|
||||
// middleware that is specific to this router
|
||||
router.use(function setUserId (req, res, next) {
|
||||
if(userId = req.headers.userId){
|
||||
|
||||
//Session key is required to continue
|
||||
if(!req.headers.sessionId){
|
||||
next('Unauthorized')
|
||||
}
|
||||
|
||||
if(req.headers.userId){
|
||||
userId = req.headers.userId
|
||||
masterKey = req.headers.masterKey
|
||||
next()
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
router.post('/search', function (req, res) {
|
||||
|
||||
@@ -10,12 +10,17 @@ let masterKey = null
|
||||
|
||||
// middleware that is specific to this router
|
||||
router.use(function setUserId (req, res, next) {
|
||||
|
||||
//Session key is required to continue
|
||||
if(!req.headers.sessionId){
|
||||
next('Unauthorized')
|
||||
}
|
||||
|
||||
if(req.headers.userId){
|
||||
userId = req.headers.userId
|
||||
masterKey = req.headers.masterKey
|
||||
next()
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
//
|
||||
|
||||
@@ -1,17 +1,60 @@
|
||||
var express = require('express')
|
||||
var router = express.Router()
|
||||
const rateLimit = require('express-rate-limit')
|
||||
|
||||
const Note = require('@models/Note')
|
||||
const User = require('@models/User')
|
||||
|
||||
|
||||
|
||||
let Note = require('@models/Note')
|
||||
|
||||
//
|
||||
// Public Note action
|
||||
//
|
||||
router.post('/opensharednote', function (req, res) {
|
||||
const sharedNoteLimiter = rateLimit({
|
||||
windowMs: 30 * 60 * 1000, //30 min window
|
||||
max: 50, // start blocking after 50 requests
|
||||
message:'Unable to open that many shared notes'
|
||||
})
|
||||
router.post('/opensharednote', sharedNoteLimiter, function (req, res) {
|
||||
|
||||
Note.getShared(req.body.noteId, req.body.sharedKey)
|
||||
.then(results => res.send(results))
|
||||
})
|
||||
|
||||
//
|
||||
// Login User
|
||||
//
|
||||
const loginLimiter = rateLimit({
|
||||
windowMs: 30 * 60 * 1000, // 30 min window
|
||||
max: 25, // start blocking after 25 requests
|
||||
message:'Please try to login again later'
|
||||
})
|
||||
router.post('/login', loginLimiter, function (req, res) {
|
||||
|
||||
User.login(req.body.username, req.body.password, req.body.authToken)
|
||||
.then( returnData => {
|
||||
|
||||
res.send(returnData)
|
||||
})
|
||||
})
|
||||
|
||||
//
|
||||
// Register User
|
||||
//
|
||||
const registerLimiter = rateLimit({
|
||||
windowMs: 60 * 60 * 1000, // 1 hour window
|
||||
max: 5, // start blocking after 5 requests
|
||||
message:'Please try again to create an acount in an hour'
|
||||
})
|
||||
router.post('/register', registerLimiter, function (req, res) {
|
||||
|
||||
User.register(req.body.username, req.body.password)
|
||||
.then( returnData => {
|
||||
|
||||
res.send(returnData)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -6,13 +6,19 @@ let QuickNote = require('@models/QuickNote');
|
||||
let userId = null
|
||||
let masterKey = null
|
||||
|
||||
// middleware that is specific to this router
|
||||
router.use(function setUserId (req, res, next) {
|
||||
|
||||
//Session key is required to continue
|
||||
if(!req.headers.sessionId){
|
||||
next('Unauthorized')
|
||||
}
|
||||
|
||||
if(req.headers.userId){
|
||||
userId = req.headers.userId
|
||||
masterKey = req.headers.masterKey
|
||||
next()
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
//Get quick note text
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
var express = require('express')
|
||||
var router = express.Router()
|
||||
|
||||
let Tags = require('@models/Tag');
|
||||
let Tags = require('@models/Tag')
|
||||
|
||||
let userId = null
|
||||
let masterKey = null
|
||||
|
||||
// middleware that is specific to this router
|
||||
router.use(function setUserId (req, res, next) {
|
||||
if(userId = req.headers.userId){
|
||||
|
||||
//Session key is required to continue
|
||||
if(!req.headers.sessionId){
|
||||
next('Unauthorized')
|
||||
}
|
||||
|
||||
if(req.headers.userId){
|
||||
userId = req.headers.userId
|
||||
masterKey = req.headers.masterKey
|
||||
next()
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
//Get the latest notes the user has created
|
||||
|
||||
@@ -5,20 +5,24 @@ const User = require('@models/User')
|
||||
const Auth = require('@helpers/Auth')
|
||||
const cs = require('@helpers/CryptoString')
|
||||
|
||||
let userId = null
|
||||
let masterKey = null
|
||||
|
||||
// middleware that is specific to this router
|
||||
router.use(function timeLog (req, res, next) {
|
||||
// console.log('Time: ', Date.now())
|
||||
next()
|
||||
router.use(function setUserId (req, res, next) {
|
||||
|
||||
//Session key is required to continue
|
||||
if(!req.headers.sessionId){
|
||||
next('Unauthorized')
|
||||
}
|
||||
|
||||
if(req.headers.userId){
|
||||
userId = req.headers.userId
|
||||
masterKey = req.headers.masterKey
|
||||
next()
|
||||
}
|
||||
})
|
||||
|
||||
// Login User
|
||||
router.post('/login', function (req, res) {
|
||||
|
||||
User.login(req.body.username, req.body.password, req.body.authToken)
|
||||
.then( returnData => {
|
||||
res.send(returnData)
|
||||
})
|
||||
})
|
||||
// Logout User
|
||||
router.post('/logout', function (req, res) {
|
||||
|
||||
@@ -28,19 +32,6 @@ router.post('/logout', function (req, res) {
|
||||
})
|
||||
})
|
||||
|
||||
// Register User
|
||||
router.post('/register', function (req, res) {
|
||||
|
||||
User.register(req.body.username, req.body.password)
|
||||
.then( returnData => {
|
||||
|
||||
res.send(returnData)
|
||||
})
|
||||
.catch(e => {
|
||||
res.send(false)
|
||||
})
|
||||
})
|
||||
|
||||
// change password
|
||||
router.post('/changepassword', function (req, res) {
|
||||
|
||||
|
||||
Reference in new issue
Block a user