* Added theme colors to form fields
* Added some basic table styles for inserting some shitty tables * Made popup notification styles look better and work better on mobile * Quick note now opens a note and not some weird page * Menu collapses when page is small, behaves like mobile menu * Added terms and conditions to help and login forms * Added password change functionality * Better styles for shared page * Added some tests for changing password
This commit is contained in:
parent
47fff0e1ee
commit
e7d1cc7bc9
Binary file not shown.
Before Width: | Height: | Size: 6.7 KiB |
@ -86,6 +86,8 @@ body {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.ui.form input:not([type]),
|
.ui.form input:not([type]),
|
||||||
.ui.form input:not([type]):focus,
|
.ui.form input:not([type]):focus,
|
||||||
.ui.form textarea:not([type]),
|
.ui.form textarea:not([type]),
|
||||||
@ -94,6 +96,21 @@ body {
|
|||||||
background-color: var(--small_element_bg_color);
|
background-color: var(--small_element_bg_color);
|
||||||
border-color: var(--dark_border_color);
|
border-color: var(--dark_border_color);
|
||||||
}
|
}
|
||||||
|
.ui.form input[type="password"],
|
||||||
|
.ui.form input[type="text"],
|
||||||
|
.ui.input > input {
|
||||||
|
color: var(--text_color);
|
||||||
|
background-color: var(--small_element_bg_color);
|
||||||
|
border-color: var(--dark_border_color);
|
||||||
|
}
|
||||||
|
.ui.form input[type="password"]:focus, .ui.form input[type="password"]:active,
|
||||||
|
.ui.form input[type="text"]:focus, .ui.form input[type="text"]:active,
|
||||||
|
.ui.input > input:focus, .ui.input > input:active {
|
||||||
|
color: var(--text_color);
|
||||||
|
background-color: var(--small_element_bg_color);
|
||||||
|
border-color: var(--main-accent);
|
||||||
|
border-right-color: var(--main-accent) !important;
|
||||||
|
}
|
||||||
.ui.basic.label, .ui.header, .ui.header div.sub.header {
|
.ui.basic.label, .ui.header, .ui.header div.sub.header {
|
||||||
color: var(--text_color);
|
color: var(--text_color);
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
@ -358,10 +375,15 @@ i.green.icon.icon.icon.icon {
|
|||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
th, td {
|
th, td {
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
border-bottom: 1px solid #ddd;
|
border-bottom: 1px solid #ddd;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
/* table:hover th, table:hover td {
|
/* table:hover th, table:hover td {
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
@ -371,6 +393,22 @@ i.green.icon.icon.icon.icon {
|
|||||||
padding: 3px;
|
padding: 3px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
.table-tic-table {
|
||||||
|
}
|
||||||
|
.table-tic-table > div {
|
||||||
|
height: 21px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.tabletic {
|
||||||
|
display: inline-block;
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 2px;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin: 0 1px 1px 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.t-table {
|
.t-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -430,7 +468,7 @@ i.green.icon.icon.icon.icon {
|
|||||||
|
|
||||||
font-family: 'Icons';
|
font-family: 'Icons';
|
||||||
content: "\f058";
|
content: "\f058";
|
||||||
color: #21BA45;
|
color: var(--main-accent);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,30 +2,31 @@
|
|||||||
|
|
||||||
.popup-body {
|
.popup-body {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 15px;
|
top: 15px;
|
||||||
left: 15px;
|
left: 15px;
|
||||||
min-height: 50px;
|
min-height: 50px;
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
max-width: calc(100% - 20px);
|
max-width: calc(100% - 30px);
|
||||||
z-index: 1002;
|
z-index: 1002;
|
||||||
|
|
||||||
border-top: 2px solid #21ba45;
|
|
||||||
box-shadow: 0px 0px 5px 2px rgba(140,140,140,1);
|
box-shadow: 0px 0px 5px 2px rgba(140,140,140,1);
|
||||||
border-top-right-radius: 4px;
|
border-radius: 4px;
|
||||||
border-top-left-radius: 4px;
|
|
||||||
|
color: white;
|
||||||
|
background-color: var(--main-accent);
|
||||||
}
|
}
|
||||||
.popup-row {
|
.popup-row {
|
||||||
padding: 1em 5px;
|
padding: 1em 5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
white-space: nowrap;
|
|
||||||
}
|
}
|
||||||
.popup-row > span {
|
.popup-row > span {
|
||||||
width: calc(100% - 50px);
|
/*width: calc(100% - 50px);*/
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-align: center;
|
text-align: left;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 0 10px 0;
|
padding: 0 10px 0;
|
||||||
font-size: 1.25em;
|
font-size: 1.25em;
|
||||||
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
.popup-row + .popup-row {
|
.popup-row + .popup-row {
|
||||||
border-top: 1px solid #FFF;
|
border-top: 1px solid #FFF;
|
||||||
@ -36,12 +37,10 @@
|
|||||||
}
|
}
|
||||||
@keyframes slide-in-bottom {
|
@keyframes slide-in-bottom {
|
||||||
0% {
|
0% {
|
||||||
transform: translateY(1000px);
|
transform: translateY(-1000px);
|
||||||
opacity: 0;
|
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
opacity: 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,14 +62,46 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.meter {
|
||||||
|
height: 2px;
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meter span {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
background-color: white;
|
||||||
|
animation: progressBar 3s linear;
|
||||||
|
animation-fill-mode: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes progressBar {
|
||||||
|
0% { width: 0; }
|
||||||
|
100% { width: 100%; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="popup-body slide-in-bottom" v-on:click="dismiss" v-if="notifications.length > 0">
|
<div class="popup-body slide-in-bottom" v-on:click="dismiss" v-if="notifications.length > 0">
|
||||||
<div class="popup-row color-fade" v-for="item in notifications">
|
<div class="popup-row" v-for="item in notifications">
|
||||||
<span>{{ item }}</span>
|
<div class="meter">
|
||||||
|
<span><span class="progress"></span></span>
|
||||||
|
</div>
|
||||||
|
<span><i class="small info circle icon"></i>{{ item }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -94,9 +125,9 @@
|
|||||||
},
|
},
|
||||||
mounted(){
|
mounted(){
|
||||||
|
|
||||||
// this.$bus.$emit('notification', 'Password Protection Removed')
|
// this.$bus.$emit('notification', 'Password Protection Removed Login did not succeed')
|
||||||
// this.$bus.$emit('notification', 'Password Protection Removed')
|
// this.$bus.$emit('notification', 'Password Protection Removed your life is exposed to the internet')
|
||||||
// this.$bus.$emit('notification', 'Password Protection Removed')
|
// this.$bus.$emit('notification', 'Password Protection Removed everyone can see everything')
|
||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -105,7 +136,7 @@
|
|||||||
clearTimeout(this.totalTimeout)
|
clearTimeout(this.totalTimeout)
|
||||||
this.totalTimeout = setTimeout(() => {
|
this.totalTimeout = setTimeout(() => {
|
||||||
this.dismiss()
|
this.dismiss()
|
||||||
}, 4000)
|
}, 3000)
|
||||||
},
|
},
|
||||||
dismiss(){
|
dismiss(){
|
||||||
this.notifications = []
|
this.notifications = []
|
||||||
|
@ -133,9 +133,23 @@
|
|||||||
|
|
||||||
<div class="mobile-button"></div>
|
<div class="mobile-button"></div>
|
||||||
|
|
||||||
<router-link v-if="loggedIn" to="/quick" class="mobile-button" exact-active-class="active">
|
<!-- open straight to note -->
|
||||||
|
<router-link
|
||||||
|
v-if="loggedIn && $store.getters.totals && $store.getters.totals['quickNote']"
|
||||||
|
exact-active-class="active"
|
||||||
|
class="mobile-button"
|
||||||
|
:to="`/notes/open/${$store.getters.totals['quickNote']}`">
|
||||||
<i class="green sticky note outline icon"></i>
|
<i class="green sticky note outline icon"></i>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
|
<!-- create new and redirect to new note id -->
|
||||||
|
<a
|
||||||
|
v-if="loggedIn && $store.getters.totals && !$store.getters.totals['quickNote']"
|
||||||
|
v-on:click="newQuickNote()"
|
||||||
|
exact-active-class="active"
|
||||||
|
class="mobile-button">
|
||||||
|
<i class="green sticky note outline icon"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
<router-link v-if="loggedIn" class="mobile-button" exact-active-class="active" to="/notes" v-on:click.native="emitReloadEvent()">
|
<router-link v-if="loggedIn" class="mobile-button" exact-active-class="active" to="/notes" v-on:click.native="emitReloadEvent()">
|
||||||
<logo class="logo-display" color="var(--main-accent)" />
|
<logo class="logo-display" color="var(--main-accent)" />
|
||||||
@ -167,10 +181,8 @@
|
|||||||
<div class="global-menu" v-if="!collapsed" v-on:click="menuClicked">
|
<div class="global-menu" v-if="!collapsed" v-on:click="menuClicked">
|
||||||
|
|
||||||
<div class="menu-section" v-on:click="collapseMenu">
|
<div class="menu-section" v-on:click="collapseMenu">
|
||||||
<!-- <div class="menu-item menu-button" > -->
|
<i class="white angle left icon"></i>
|
||||||
<i class="white angle left icon"></i>
|
<logo class="menu-logo-display" color="var(--main-accent)" />
|
||||||
<logo class="menu-logo-display" color="var(--main-accent)" />
|
|
||||||
<!-- </div> -->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="menu-section" v-if="loggedIn">
|
<div class="menu-section" v-if="loggedIn">
|
||||||
@ -216,9 +228,26 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="menu-section" v-if="loggedIn">
|
<div class="menu-section" v-if="loggedIn">
|
||||||
<router-link v-if="loggedIn" exact-active-class="active" class="menu-item menu-button" to="/quick">
|
|
||||||
|
|
||||||
|
<!-- open straight to note -->
|
||||||
|
<router-link
|
||||||
|
v-if="loggedIn && $store.getters.totals && $store.getters.totals['quickNote']"
|
||||||
|
exact-active-class="active"
|
||||||
|
class="menu-item menu-button"
|
||||||
|
:to="`/notes/open/${$store.getters.totals['quickNote']}`">
|
||||||
<i class="sticky note outline icon"></i>Scratch Pad
|
<i class="sticky note outline icon"></i>Scratch Pad
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
|
<!-- create new and redirect to new note id -->
|
||||||
|
<a
|
||||||
|
v-if="loggedIn && $store.getters.totals && !$store.getters.totals['quickNote']"
|
||||||
|
v-on:click="newQuickNote()"
|
||||||
|
exact-active-class="active"
|
||||||
|
class="menu-item menu-button">
|
||||||
|
<i class="sticky note outline icon"></i>Scratch Pad
|
||||||
|
</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="menu-section" v-if="!loggedIn">
|
<div class="menu-section" v-if="!loggedIn">
|
||||||
@ -244,7 +273,7 @@
|
|||||||
|
|
||||||
<div class="menu-section">
|
<div class="menu-section">
|
||||||
<router-link class="menu-item menu-button" exact-active-class="active" to="/help">
|
<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 | Terms
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -304,6 +333,8 @@
|
|||||||
this.$store.dispatch('fetchAndUpdateUserTotals')
|
this.$store.dispatch('fetchAndUpdateUserTotals')
|
||||||
this.version = localStorage.getItem('currentVersion')
|
this.version = localStorage.getItem('currentVersion')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.resizeEventHandler() //Trigger resize event
|
||||||
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -329,16 +360,29 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
newQuickNote(){
|
||||||
|
|
||||||
|
axios.post('/api/quick-note/get')
|
||||||
|
.then( ({data}) => {
|
||||||
|
|
||||||
|
console.log(data)
|
||||||
|
this.$router.push({'path':'/notes/open/'+data.noteId})
|
||||||
|
})
|
||||||
|
|
||||||
|
},
|
||||||
resizeEventHandler(e) {
|
resizeEventHandler(e) {
|
||||||
clearTimeout(this.resizeDebounce)
|
clearTimeout(this.resizeDebounce)
|
||||||
this.resizeDebounce = setTimeout(() => {
|
this.resizeDebounce = setTimeout(() => {
|
||||||
|
|
||||||
|
this.mobile = false
|
||||||
this.menuOpen = false
|
this.menuOpen = false
|
||||||
this.collapsed = false
|
this.collapsed = false
|
||||||
|
|
||||||
if(window.innerWidth < 700){
|
if(window.innerWidth < 700){
|
||||||
|
|
||||||
this.collapsed = true
|
this.collapsed = true
|
||||||
|
this.mobile = true
|
||||||
|
|
||||||
}
|
}
|
||||||
}, 100)
|
}, 100)
|
||||||
},
|
},
|
||||||
|
@ -69,10 +69,6 @@
|
|||||||
<i class="grip lines icon"></i>
|
<i class="grip lines icon"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="edit-button" v-on:click="insertTable(4,4)" data-tooltip="Insert Table" data-position="bottom center" data-inverted>
|
|
||||||
<i class="book dead 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" data-inverted>
|
||||||
<i class="remove format icon"></i>
|
<i class="remove format icon"></i>
|
||||||
</div>
|
</div>
|
||||||
@ -276,7 +272,14 @@
|
|||||||
<!-- create table option -->
|
<!-- create table option -->
|
||||||
<side-slide-menu v-if="table" v-on:close="table = false; fetchNoteTags()" name="table" :style-object="styleObject">
|
<side-slide-menu v-if="table" v-on:close="table = false; fetchNoteTags()" name="table" :style-object="styleObject">
|
||||||
<div class="ui basic segment">
|
<div class="ui basic segment">
|
||||||
Create a table
|
<h2>Insert Table</h2>
|
||||||
|
<div class="table-tic-table">
|
||||||
|
<div v-for="i in 10">
|
||||||
|
<div v-for="j in 10" class="tabletic" v-on:click="insertTable(i,j)">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</side-slide-menu>
|
</side-slide-menu>
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li>Shared notes can be read and edited by you and all shared users.</li>
|
<li>Shared notes can be read and edited by you and all shared users.</li>
|
||||||
<li>Shared notes can only be shared by the creator of the note.</li>
|
<li>Shared notes can only be shared by the creator of the note.</li>
|
||||||
|
<li>If you turn off sharing, no one else can read the note.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -345,62 +345,26 @@ const SquireButtonFunctions = {
|
|||||||
this.editor.focus()
|
this.editor.focus()
|
||||||
this.editor.moveCursorToEnd()
|
this.editor.moveCursorToEnd()
|
||||||
},
|
},
|
||||||
insertTable(wide, tall){
|
insertTable(tall, wide){
|
||||||
console.log('Insert a table')
|
console.log(`Table: ${wide} x ${tall}`)
|
||||||
|
|
||||||
let tableSyntax = `
|
//Insert a table
|
||||||
<div>
|
let tableSyntax = '<div>'
|
||||||
<table>
|
tableSyntax += '<table>'
|
||||||
<tr>
|
|
||||||
<th><p><br></p></th>
|
|
||||||
<th><p><br></p></th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><p><br></p></td>
|
|
||||||
<td><p><br></p></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><p><br></p></td>
|
|
||||||
<td><p><br></p></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
|
|
||||||
tableSyntax = `
|
|
||||||
<span class="t-table">
|
|
||||||
<span>
|
|
||||||
<span>
|
|
||||||
<p><br></p>
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
<p><br></p>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
<span>
|
|
||||||
<p><br></p>
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
<p><br></p>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<p><br></p>
|
|
||||||
`
|
|
||||||
tableSyntax = ''
|
|
||||||
tableSyntax += '<span class="t-table">'
|
|
||||||
for (let i = 0; i < tall; i++) {
|
for (let i = 0; i < tall; i++) {
|
||||||
|
tableSyntax += '<tr>'
|
||||||
for (let j = 0; j < wide; j++) {
|
for (let j = 0; j < wide; j++) {
|
||||||
|
tableSyntax += '<td><p><br></p></td>'
|
||||||
}
|
}
|
||||||
|
tableSyntax += '</tr>'
|
||||||
}
|
}
|
||||||
tableSyntax += '</span><p><br></p>'
|
tableSyntax += '</table></div><p><br></p>'
|
||||||
|
|
||||||
this.editor.insertHTML(tableSyntax)
|
this.editor.insertHTML(tableSyntax)
|
||||||
this.editor.focus()
|
this.editor.focus()
|
||||||
this.editor.moveCursorToEnd()
|
this.editor.moveCursorToEnd()
|
||||||
|
|
||||||
|
this.$router.go(-1)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@ -8,6 +8,23 @@
|
|||||||
Settings
|
Settings
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
<!-- Accent Color -->
|
<!-- Accent Color -->
|
||||||
<div class="ui segment">
|
<div class="ui segment">
|
||||||
<div class="ui grid">
|
<div class="ui grid">
|
||||||
@ -65,20 +82,20 @@
|
|||||||
<div class="five wide column">
|
<div class="five wide column">
|
||||||
<label>Current Password</label>
|
<label>Current Password</label>
|
||||||
<div class="ui fluid input">
|
<div class="ui fluid input">
|
||||||
<input type="text" placeholder="Current Password">
|
<input v-model="change1" type="password" placeholder="Current Password">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="five wide column">
|
<div class="five wide column">
|
||||||
<label>New Password</label>
|
<label>New Password</label>
|
||||||
<div class="ui fluid input">
|
<div class="ui fluid input">
|
||||||
<input type="text" placeholder="New Password">
|
<input v-model="change2" type="password" placeholder="New Password">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="six wide column">
|
<div class="six wide column">
|
||||||
<label>Rereat New Password</label>
|
<label>Rereat New Password</label>
|
||||||
<div class="ui fluid action input">
|
<div class="ui fluid action input">
|
||||||
<input type="text" placeholder="Repeat Password">
|
<input v-model="change3" type="password" placeholder="Repeat Password">
|
||||||
<div class="ui green button">
|
<div v-on:click="passwordChange()" class="ui button" :class="{'green':(change1.length > 0 && change2 == change3)}">
|
||||||
Change it!
|
Change it!
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -98,7 +115,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="eight wide column">
|
<div class="eight wide column">
|
||||||
<div class="ui button">
|
<div class="ui button" v-on:click="revokeAllSessions()">
|
||||||
Log Out all other browsers
|
Log Out all other browsers
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -123,6 +140,7 @@
|
|||||||
password: '',
|
password: '',
|
||||||
qrCode: '',
|
qrCode: '',
|
||||||
verificationToken: '',
|
verificationToken: '',
|
||||||
|
showNewNoteConfirm: false,
|
||||||
|
|
||||||
themeColors: [
|
themeColors: [
|
||||||
'#21BA45', //Green
|
'#21BA45', //Green
|
||||||
@ -137,10 +155,26 @@
|
|||||||
'#fbbd08', //Yellow
|
'#fbbd08', //Yellow
|
||||||
'#767676', //Grey
|
'#767676', //Grey
|
||||||
'#303030', //Black-almost
|
'#303030', //Black-almost
|
||||||
]
|
],
|
||||||
|
|
||||||
|
change1: '',
|
||||||
|
change2: '',
|
||||||
|
change3: '',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
newQuickNote(){
|
||||||
|
|
||||||
|
this.showNewNoteConfirm = true
|
||||||
|
|
||||||
|
axios.post('/api/quick-note/new')
|
||||||
|
.then( ({data}) => {
|
||||||
|
this.showNewNoteConfirm = false
|
||||||
|
this.$store.dispatch('fetchAndUpdateUserTotals')
|
||||||
|
this.$bus.$emit('notification', 'New Scratch Pad Created')
|
||||||
|
})
|
||||||
|
|
||||||
|
},
|
||||||
logout() {
|
logout() {
|
||||||
this.$store.commit('destroyLoginToken')
|
this.$store.commit('destroyLoginToken')
|
||||||
this.$router.push('/')
|
this.$router.push('/')
|
||||||
@ -176,6 +210,47 @@
|
|||||||
//It failed
|
//It failed
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
passwordChange(){
|
||||||
|
|
||||||
|
if(this.change1 == '' || this.change2 == '' || this.change3 == ''){
|
||||||
|
this.$bus.$emit('notification', 'All Password Fields Required')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.change1 == this.change2){
|
||||||
|
this.$bus.$emit('notification', 'Old password matches new password')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.change2 != this.change3){
|
||||||
|
this.$bus.$emit('notification', 'New Passwords do not match')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const postData = {
|
||||||
|
'currentPass':this.change1,
|
||||||
|
'newPass':this.change3
|
||||||
|
}
|
||||||
|
|
||||||
|
axios.post('/api/user/changepassword', postData)
|
||||||
|
.then(({data}) => {
|
||||||
|
if(data){
|
||||||
|
this.$bus.$emit('notification', 'Success: Password Changed')
|
||||||
|
this.change1 = ''
|
||||||
|
this.change2 = ''
|
||||||
|
this.change3 = ''
|
||||||
|
} else {
|
||||||
|
this.$bus.$emit('notification', 'Failed to change password')
|
||||||
|
this.change1 = ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
revokeAllSessions(){
|
||||||
|
axios.post('/api/user/revokesessions')
|
||||||
|
.then(({data}) => {
|
||||||
|
this.$bus.$emit('notification', 'All other active sessions revoked.')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,38 +4,55 @@
|
|||||||
<div class="sixteen wide column"></div>
|
<div class="sixteen wide column"></div>
|
||||||
|
|
||||||
<div class="sixteen wide column" v-if="text.length > 0 || title.length > 0">
|
<div class="sixteen wide column" v-if="text.length > 0 || title.length > 0">
|
||||||
<div class="ui text container squire-box" :style="{ 'background-color':styleObject['noteBackground'], 'color':styleObject['noteText']}">
|
<div class="ui text container">
|
||||||
|
|
||||||
<h1 v-if="title">{{title}}</h1>
|
<div class="ui segment" :style="{ 'background-color':styleObject['noteBackground'], 'color':styleObject['noteText']}">
|
||||||
|
|
||||||
<div v-if="text" v-html="text"></div>
|
<h1 v-if="title">{{title}}</h1>
|
||||||
|
|
||||||
|
<div v-if="text" v-html="text" class="squire-box"></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="sixteen wide column" v-if="!$store.getters.getLoggedIn">
|
<div class="sixteen wide column" v-if="!$store.getters.getLoggedIn">
|
||||||
<div class="ui text container">
|
<div class="ui text container">
|
||||||
<h2 class="ui header">
|
|
||||||
<img class="small-logo" loading="lazy" src="/api/static/assets/logo.svg" alt="Solid Scribe Logo">
|
<div class="ui segment">
|
||||||
<div class="content">
|
|
||||||
Solid Scribe is an easy, free, secure Note App
|
<div class="ui grid">
|
||||||
<div class="sub header">
|
<div class="three wide middle aligned center aligned column">
|
||||||
Encrypted notes, only readable by you. Unless you share them.
|
<img class="small-logo" loading="lazy" src="/api/static/assets/logo.svg" alt="Solid Scribe Logo">
|
||||||
|
</div>
|
||||||
|
<div class="thirteen wide column">
|
||||||
|
<!-- header -->
|
||||||
|
<h2 class="ui header">
|
||||||
|
<div class="content">
|
||||||
|
Solid Scribe is an easy, free, secure Note App
|
||||||
|
<div class="sub header">
|
||||||
|
Encrypted notes, only readable by you. Unless you share them.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</h2>
|
||||||
|
<!-- buttons -->
|
||||||
|
<div class="ui grid">
|
||||||
|
<div class="eight wide center aligned column">
|
||||||
|
<router-link class="ui compact green button" to="/login">
|
||||||
|
<i class="plug icon"></i>Sign Up
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
<div class="eight wide center aligned column">
|
||||||
|
<router-link class="ui compact green button" to="/">
|
||||||
|
<i class="comment outline icon"></i>
|
||||||
|
Learn More
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</h2>
|
|
||||||
<div class="ui grid">
|
|
||||||
<div class="eight wide center aligned column">
|
|
||||||
<router-link class="ui compact green button" to="/login">
|
|
||||||
<i class="plug icon"></i>Sign Up
|
|
||||||
</router-link>
|
|
||||||
</div>
|
|
||||||
<div class="eight wide center aligned column">
|
|
||||||
<router-link class="ui compact green button" to="/">
|
|
||||||
<i class="comment outline icon"></i>
|
|
||||||
Learn More
|
|
||||||
</router-link>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -99,7 +116,7 @@
|
|||||||
|
|
||||||
<style type="text/css" scoped>
|
<style type="text/css" scoped>
|
||||||
.small-logo {
|
.small-logo {
|
||||||
width: 30px;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -7,6 +7,7 @@ let Auth = {}
|
|||||||
|
|
||||||
const tokenSecretKey = process.env.JSON_KEY
|
const tokenSecretKey = process.env.JSON_KEY
|
||||||
|
|
||||||
|
//Creates session token
|
||||||
Auth.createToken = (userId, masterKey, pastId = null, pastCreatedDate = null) => {
|
Auth.createToken = (userId, masterKey, pastId = null, pastCreatedDate = null) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
@ -42,6 +43,7 @@ Auth.createToken = (userId, masterKey, pastId = null, pastCreatedDate = null) =>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Decodes session token
|
||||||
Auth.decodeToken = (token, request = null) => {
|
Auth.decodeToken = (token, request = null) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
@ -263,7 +263,7 @@ let AuthTest = require('@helpers/Auth')
|
|||||||
Auth.testTwoFactor()
|
Auth.testTwoFactor()
|
||||||
|
|
||||||
Auth.test()
|
Auth.test()
|
||||||
UserTest.keyPairTest('genMan16', '1', printResults)
|
UserTest.keyPairTest('genMan23', '1', printResults)
|
||||||
.then( ({testUserId, masterKey}) => NoteTest.test(testUserId, masterKey, printResults))
|
.then( ({testUserId, masterKey}) => NoteTest.test(testUserId, masterKey, printResults))
|
||||||
.then( message => {
|
.then( message => {
|
||||||
if(printResults) console.log(message)
|
if(printResults) console.log(message)
|
||||||
|
@ -10,19 +10,28 @@ QuickNote.get = (userId, masterKey) => {
|
|||||||
|
|
||||||
db.promise()
|
db.promise()
|
||||||
.query(`
|
.query(`
|
||||||
SELECT note.id FROM note WHERE quick_note = 1 AND user_id = ? LIMIT 1
|
SELECT note.id FROM note WHERE quick_note = 1 AND user_id = ? LIMIT 1`, [userId])
|
||||||
`, [userId])
|
|
||||||
.then((rows, fields) => {
|
.then((rows, fields) => {
|
||||||
|
|
||||||
//Quick Note is set, return note text
|
//Quick Note is set, return note text
|
||||||
if(rows[0][0] != undefined){
|
if(rows[0][0] != undefined){
|
||||||
|
|
||||||
let noteId = rows[0][0].id
|
let noteId = rows[0][0].id
|
||||||
Note.get(userId, noteId, masterKey)
|
return resolve({'noteId':noteId})
|
||||||
.then( noteObject => {
|
|
||||||
return resolve(noteObject)
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
return resolve(null)
|
//Or create a new note and get the id
|
||||||
|
let finalId = null
|
||||||
|
return Note.create(userId, 'Scratch Pad', '', masterKey)
|
||||||
|
.then(insertedId => {
|
||||||
|
finalId = insertedId
|
||||||
|
db.promise().query('UPDATE note SET quick_note = 1 WHERE id = ? AND user_id = ?',[insertedId, userId])
|
||||||
|
.then((rows, fields) => {
|
||||||
|
|
||||||
|
return resolve({'noteId':finalId})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ const speakeasy = require('speakeasy')
|
|||||||
|
|
||||||
let User = module.exports = {}
|
let User = module.exports = {}
|
||||||
|
|
||||||
const version = '3.1.3'
|
const version = '3.1.5'
|
||||||
|
|
||||||
//Login a user, if that user does not exist create them
|
//Login a user, if that user does not exist create them
|
||||||
//Issues login token
|
//Issues login token
|
||||||
@ -238,7 +238,7 @@ User.getCounts = (userId) => {
|
|||||||
|
|
||||||
Object.assign(countTotals, rows[0][0]) //combine results
|
Object.assign(countTotals, rows[0][0]) //combine results
|
||||||
|
|
||||||
return db.promise().query('SELECT two_fa_enabled FROM user WHERE id = ?', [userId])
|
return db.promise().query('SELECT id AS quickNote FROM note WHERE quick_note = 1 AND user_id = ?', [userId])
|
||||||
|
|
||||||
}).then( (rows, fields) => {
|
}).then( (rows, fields) => {
|
||||||
|
|
||||||
@ -432,6 +432,66 @@ User.getByUserName = (username) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
User.changePassword = (userId, oldPass, newPass) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
User.getMasterKey(userId, oldPass)
|
||||||
|
.then(masterKey => {
|
||||||
|
User.getPrivateKey(userId, masterKey)
|
||||||
|
.then(privateKey => {
|
||||||
|
//If success, user has correct password
|
||||||
|
|
||||||
|
// Generate new master pass, encrypt with new password
|
||||||
|
// const masterPassword = cs.createSmallSalt()
|
||||||
|
const salt = cs.createSmallSalt()
|
||||||
|
const encryptedMasterPassword = cs.encrypt(newPass, salt, masterKey)
|
||||||
|
const encryptedPrivateKey = cs.encrypt(masterKey, salt, privateKey)
|
||||||
|
|
||||||
|
db.promise()
|
||||||
|
.query(
|
||||||
|
'UPDATE user_key SET salt = ?, `key` = ?, private_key_encrypted = ? WHERE user_id = ? LIMIT 1',
|
||||||
|
[salt, encryptedMasterPassword, encryptedPrivateKey, userId]
|
||||||
|
).then((r,f) => {
|
||||||
|
//Create login using password
|
||||||
|
let shasum = crypto.createHash('sha512') //Prepare Hash
|
||||||
|
const saltString = shasum.digest('hex')
|
||||||
|
const passwordSalt = Buffer.from(saltString, 'binary') //Generate Salt hash
|
||||||
|
const iterations = 25000
|
||||||
|
|
||||||
|
crypto.pbkdf2(newPass, passwordSalt, iterations, 512, 'sha512', function(err, delivered_key) {
|
||||||
|
|
||||||
|
const deliveredPass = delivered_key.toString('hex')
|
||||||
|
|
||||||
|
db.promise().query('UPDATE user SET password = ?, salt = ? WHERE id = ? LIMIT 1', [deliveredPass, passwordSalt, userId])
|
||||||
|
.then((r,f) => {
|
||||||
|
return resolve(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
resolve(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
User.revokeActiveSessions = (userId, sessionId) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
const userHash = cs.hash(String(userId)).toString('base64')
|
||||||
|
|
||||||
|
db.promise().query('DELETE FROM user_active_session WHERE user_hash = ? AND session_id != ?', [userHash, sessionId])
|
||||||
|
.then((r,f) => {
|
||||||
|
|
||||||
|
resolve(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
User.deleteUser = (userId, password) => {
|
User.deleteUser = (userId, password) => {
|
||||||
|
|
||||||
//Verify user is correct by decryptig master key with password
|
//Verify user is correct by decryptig master key with password
|
||||||
@ -471,6 +531,7 @@ User.keyPairTest = (testUserName = 'genMan', password = '1', printResults) => {
|
|||||||
|
|
||||||
const randomUsername = Math.random().toString(36).substring(2, 15);
|
const randomUsername = Math.random().toString(36).substring(2, 15);
|
||||||
const randomPassword = '1'
|
const randomPassword = '1'
|
||||||
|
const secondPassword = '2'
|
||||||
|
|
||||||
User.register(testUserName, password)
|
User.register(testUserName, password)
|
||||||
.then( ({ token, userId }) => {
|
.then( ({ token, userId }) => {
|
||||||
@ -478,7 +539,7 @@ User.keyPairTest = (testUserName = 'genMan', password = '1', printResults) => {
|
|||||||
|
|
||||||
if(printResults) console.log('Test: Register User '+testUserName+' - Pass')
|
if(printResults) console.log('Test: Register User '+testUserName+' - Pass')
|
||||||
|
|
||||||
return User.getMasterKey(testUserId, password)
|
return User.getMasterKey(testUserId, password)
|
||||||
})
|
})
|
||||||
.then(newMasterKey => {
|
.then(newMasterKey => {
|
||||||
masterKey = newMasterKey
|
masterKey = newMasterKey
|
||||||
@ -510,6 +571,26 @@ User.keyPairTest = (testUserName = 'genMan', password = '1', printResults) => {
|
|||||||
|
|
||||||
if(printResults) console.log('Test: Login New User - Pass')
|
if(printResults) console.log('Test: Login New User - Pass')
|
||||||
|
|
||||||
|
return User.changePassword(testUserId, randomPassword, secondPassword)
|
||||||
|
|
||||||
|
})
|
||||||
|
.then(passwordChangeResults => {
|
||||||
|
|
||||||
|
if(printResults) console.log('Test: Password Change - ', passwordChangeResults?'Pass':'Fail')
|
||||||
|
|
||||||
|
return User.login(testUserName, secondPassword)
|
||||||
|
|
||||||
|
})
|
||||||
|
.then(reLogin => {
|
||||||
|
|
||||||
|
if(printResults) console.log('Test: Login With new Password - Pass')
|
||||||
|
|
||||||
|
return User.getMasterKey(testUserId, secondPassword)
|
||||||
|
})
|
||||||
|
.then(newMasterKey => {
|
||||||
|
|
||||||
|
masterKey = newMasterKey
|
||||||
|
|
||||||
resolve({testUserId, masterKey})
|
resolve({testUserId, masterKey})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -6,9 +6,8 @@ let QuickNote = require('@models/QuickNote');
|
|||||||
let userId = null
|
let userId = null
|
||||||
let masterKey = null
|
let masterKey = null
|
||||||
|
|
||||||
// middleware that is specific to this router
|
|
||||||
router.use(function setUserId (req, res, next) {
|
router.use(function setUserId (req, res, next) {
|
||||||
if(userId = req.headers.userId){
|
if(req.headers.userId){
|
||||||
userId = req.headers.userId
|
userId = req.headers.userId
|
||||||
masterKey = req.headers.masterKey
|
masterKey = req.headers.masterKey
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,25 @@ router.post('/register', function (req, res) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// change password
|
||||||
|
router.post('/changepassword', function (req, res) {
|
||||||
|
|
||||||
|
User.changePassword(req.headers.userId, req.body.currentPass, req.body.newPass)
|
||||||
|
.then( returnData => {
|
||||||
|
res.send(returnData)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
//Revoke all active session keys for user
|
||||||
|
router.post('/revokesessions', function(req, res) {
|
||||||
|
|
||||||
|
User.revokeActiveSessions(req.headers.userId, req.headers.sessionId)
|
||||||
|
.then( returnData => {
|
||||||
|
res.send(returnData)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
// fetch counts of users notes
|
// fetch counts of users notes
|
||||||
router.post('/totals', function (req, res) {
|
router.post('/totals', function (req, res) {
|
||||||
User.getCounts(req.headers.userId)
|
User.getCounts(req.headers.userId)
|
||||||
|
Loading…
Reference in New Issue
Block a user