Browse Source

adding form for users, adding sidebar style

master
HerrHase 3 years ago
parent
commit
c511816d25
39 changed files with 10485 additions and 131 deletions
  1. BIN
      .deno_plugins/argon2_bd2886214d1ad5713d8de93657d3eba5.so
  2. +1
    -0
      mix-manifest.json
  3. +53
    -4
      public/css/index.css
  4. +13
    -13
      public/js/bucket-single.js
  5. +6
    -6
      public/js/create-bucket.js
  6. +14
    -14
      public/js/dashboard.js
  7. +9280
    -0
      public/js/users.js
  8. +86
    -0
      resources/js/components/users.riot
  9. +128
    -0
      resources/js/components/users/form.riot
  10. +11
    -0
      resources/js/users.js
  11. +68
    -0
      resources/scss/components/_sidebar.scss
  12. +1
    -0
      resources/scss/index.scss
  13. +3
    -0
      resources/views/layout.html
  14. +15
    -0
      resources/views/users.html
  15. +57
    -0
      server-backup.ts
  16. +25
    -12
      server.ts
  17. +45
    -2
      src/http/api/bucket.ts
  18. +55
    -18
      src/http/api/buckets.ts
  19. +2
    -19
      src/http/api/note.ts
  20. +93
    -0
      src/http/api/notes.ts
  21. +149
    -0
      src/http/api/user.ts
  22. +60
    -0
      src/http/api/users.ts
  23. +43
    -0
      src/http/auth.ts
  24. +23
    -0
      src/http/buckets.ts
  25. +0
    -0
      src/http/home.ts
  26. +17
    -0
      src/http/users.ts
  27. +51
    -0
      src/middleware/bucket.ts
  28. +32
    -0
      src/middleware/session.ts
  29. +36
    -0
      src/repositories/bucket.ts
  30. +47
    -0
      src/repositories/user.ts
  31. +0
    -0
      src/respositories/user.ts
  32. +23
    -0
      src/rules/exists.ts
  33. +25
    -0
      src/rules/unique.ts
  34. +19
    -0
      src/rules/uuid.ts
  35. +3
    -1
      src/stores/user.ts
  36. +0
    -13
      src/validators/bucket.ts
  37. +0
    -15
      src/validators/note.ts
  38. +0
    -14
      src/validators/user.ts
  39. +1
    -0
      webpack.mix.js

BIN
.deno_plugins/argon2_bd2886214d1ad5713d8de93657d3eba5.so View File


+ 1
- 0
mix-manifest.json View File

@ -4,6 +4,7 @@
"/public/js/bucket-single.js": "/public/js/bucket-single.js", "/public/js/bucket-single.js": "/public/js/bucket-single.js",
"/public/js/create-bucket.js": "/public/js/create-bucket.js", "/public/js/create-bucket.js": "/public/js/create-bucket.js",
"/public/js/dashboard.js": "/public/js/dashboard.js", "/public/js/dashboard.js": "/public/js/dashboard.js",
"/public/js/users.js": "/public/js/users.js",
"/public/css/index.css": "/public/css/index.css", "/public/css/index.css": "/public/css/index.css",
"/public/css/demo.html": "/public/css/demo.html", "/public/css/demo.html": "/public/css/demo.html",
"/public/css/IBMPlexMono-Bold.eot": "/public/css/IBMPlexMono-Bold.eot", "/public/css/IBMPlexMono-Bold.eot": "/public/css/IBMPlexMono-Bold.eot",


+ 53
- 4
public/css/index.css View File

@ -2524,16 +2524,16 @@ input[type=checkbox].field-choice:checked ~ .field-switch:after {
* @link https://github.com/tentakelfabrik/plain-ui * @link https://github.com/tentakelfabrik/plain-ui
* *
*/ */
.panel {
.panel, .sidebar__inner {
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 2px; border-radius: 2px;
background-color: var(--background-contrast); background-color: var(--background-contrast);
} }
.panel__body {
.panel__body, .sidebar__body {
padding: 0.6rem 0.8rem; padding: 0.6rem 0.8rem;
} }
@media only screen and (min-width: 992px) { @media only screen and (min-width: 992px) {
.panel__body {
.panel__body, .sidebar__body {
padding: 0.7rem 1rem 0.9rem; padding: 0.7rem 1rem 0.9rem;
} }
} }
@ -14926,7 +14926,7 @@ input[type=checkbox].field-choice:checked ~ .field-switch:after {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.note-form .panel {
.note-form .panel, .note-form .sidebar__inner {
position: relative; position: relative;
z-index: 20; z-index: 20;
max-width: 33%; max-width: 33%;
@ -14955,6 +14955,55 @@ input[type=checkbox].field-choice:checked ~ .field-switch:after {
padding: 0; padding: 0;
} }
.sidebar {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
max-width: 33%;
z-index: 1;
visibility: hidden;
transition: visibility 0s linear 0.5s;
}
.sidebar__inner {
position: relative;
height: 100%;
transition: transform 0.2s;
transform: translateX(-100%);
}
.sidebar__footer {
position: fixed;
left: 0;
bottom: 0;
display: flex;
justify-content: space-between;
background: var(--background);
width: 100%;
padding: 1rem;
}
.sidebar:before {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: transparent;
transition: background-color 0.5s;
z-index: 0;
content: "";
}
.sidebar--open {
visibility: visible;
transition: visibility 0s linear 0s;
}
.sidebar--open .sidebar__inner {
transform: translateX(0);
}
.sidebar--open:before {
background: rgba(0, 0, 0, 0.7);
}
.container--app { .container--app {
max-width: 100%; max-width: 100%;
padding: 15px 30px; padding: 15px 30px;


+ 13
- 13
public/js/bucket-single.js View File

@ -107,7 +107,7 @@ __webpack_require__.r(__webpack_exports__);
getComponent getComponent
) { ) {
return template( return template(
'<div expr0="expr0" class="field-error"></div>',
'<div expr14="expr14" class="field-error"></div>',
[ [
{ {
'type': bindingTypes.IF, 'type': bindingTypes.IF,
@ -118,11 +118,11 @@ __webpack_require__.r(__webpack_exports__);
return _scope.state.errors.length > 0; return _scope.state.errors.length > 0;
}, },
'redundantAttribute': 'expr0',
'selector': '[expr0]',
'redundantAttribute': 'expr14',
'selector': '[expr14]',
'template': template( 'template': template(
'<ul><li expr1="expr1"></li></ul>',
'<ul><li expr15="expr15"></li></ul>',
[ [
{ {
'type': bindingTypes.EACH, 'type': bindingTypes.EACH,
@ -153,8 +153,8 @@ __webpack_require__.r(__webpack_exports__);
] ]
), ),
'redundantAttribute': 'expr1',
'selector': '[expr1]',
'redundantAttribute': 'expr15',
'selector': '[expr15]',
'itemName': 'error', 'itemName': 'error',
'indexName': null, 'indexName': null,
@ -272,7 +272,7 @@ riot__WEBPACK_IMPORTED_MODULE_3__.mount('field-error')
getComponent getComponent
) { ) {
return template( return template(
'<div class="note-form"><div class="panel"><div class="panel__body"><form id="form" novalidate><input expr8="expr8" type="hidden" name="_id"/><div class="field-group"><label class="field-label">\n title\n <input type="text" class="field-text" name="title"/></label></div><div class="field-group"><label class="field-label">\n content\n <textarea name="content" class="field-text"></textarea></label></div><div class><div class="tabs"></div></div><div><button expr9="expr9" class="button"></button><button expr10="expr10" class="button" type="submit"></button></div></form></div></div></div>',
'<div class="note-form"><div class="panel"><div class="panel__body"><form id="form" novalidate><input expr0="expr0" type="hidden" name="_id"/><div class="field-group"><label class="field-label">\n title\n <input type="text" class="field-text" name="title"/></label></div><div class="field-group"><label class="field-label">\n content\n <textarea name="content" class="field-text"></textarea></label></div><div class><div class="tabs"></div></div><div><button expr1="expr1" class="button"></button><button expr2="expr2" class="button" type="submit"></button></div></form></div></div></div>',
[ [
{ {
'type': bindingTypes.IF, 'type': bindingTypes.IF,
@ -283,8 +283,8 @@ riot__WEBPACK_IMPORTED_MODULE_3__.mount('field-error')
return _scope.state.note && _scope.state.note._id; return _scope.state.note && _scope.state.note._id;
}, },
'redundantAttribute': 'expr8',
'selector': '[expr8]',
'redundantAttribute': 'expr0',
'selector': '[expr0]',
'template': template( 'template': template(
null, null,
@ -315,8 +315,8 @@ riot__WEBPACK_IMPORTED_MODULE_3__.mount('field-error')
return !_scope.state.note || (_scope.state.note && !_scope.state.note._id); return !_scope.state.note || (_scope.state.note && !_scope.state.note._id);
}, },
'redundantAttribute': 'expr9',
'selector': '[expr9]',
'redundantAttribute': 'expr1',
'selector': '[expr1]',
'template': template( 'template': template(
'\n Create\n ', '\n Create\n ',
@ -332,8 +332,8 @@ riot__WEBPACK_IMPORTED_MODULE_3__.mount('field-error')
return _scope.state.note && _scope.state.note._id; return _scope.state.note && _scope.state.note._id;
}, },
'redundantAttribute': 'expr10',
'selector': '[expr10]',
'redundantAttribute': 'expr2',
'selector': '[expr2]',
'template': template( 'template': template(
'\n Save\n ', '\n Save\n ',


+ 6
- 6
public/js/create-bucket.js View File

@ -107,7 +107,7 @@ __webpack_require__.r(__webpack_exports__);
getComponent getComponent
) { ) {
return template( return template(
'<div expr0="expr0" class="field-error"></div>',
'<div expr14="expr14" class="field-error"></div>',
[ [
{ {
'type': bindingTypes.IF, 'type': bindingTypes.IF,
@ -118,11 +118,11 @@ __webpack_require__.r(__webpack_exports__);
return _scope.state.errors.length > 0; return _scope.state.errors.length > 0;
}, },
'redundantAttribute': 'expr0',
'selector': '[expr0]',
'redundantAttribute': 'expr14',
'selector': '[expr14]',
'template': template( 'template': template(
'<ul><li expr1="expr1"></li></ul>',
'<ul><li expr15="expr15"></li></ul>',
[ [
{ {
'type': bindingTypes.EACH, 'type': bindingTypes.EACH,
@ -153,8 +153,8 @@ __webpack_require__.r(__webpack_exports__);
] ]
), ),
'redundantAttribute': 'expr1',
'selector': '[expr1]',
'redundantAttribute': 'expr15',
'selector': '[expr15]',
'itemName': 'error', 'itemName': 'error',
'indexName': null, 'indexName': null,


+ 14
- 14
public/js/dashboard.js View File

@ -74,7 +74,7 @@ __webpack_require__.r(__webpack_exports__);
getComponent getComponent
) { ) {
return template( return template(
'<div class="buckets"><div class="grid"><div expr2="expr2" class="col-12 col-md-4 col-xlg-3"></div></div><div class="grid"><div class="col-12"><div class="buckets__more"><button expr7="expr7" type="button" class="button">\n More\n <svg class="icon" aria-hidden="true"><use xlink:href="/symbol-defs.svg#icon-arrow-down"/></svg></button></div></div></div></div>',
'<div class="buckets"><div class="grid"><div expr8="expr8" class="col-12 col-md-4 col-xlg-3"></div></div><div class="grid"><div class="col-12"><div class="buckets__more"><button expr13="expr13" type="button" class="button">\n More\n <svg class="icon" aria-hidden="true"><use xlink:href="/symbol-defs.svg#icon-arrow-down"/></svg></button></div></div></div></div>',
[ [
{ {
'type': bindingTypes.EACH, 'type': bindingTypes.EACH,
@ -82,11 +82,11 @@ __webpack_require__.r(__webpack_exports__);
'condition': null, 'condition': null,
'template': template( 'template': template(
'<article class="panel buckets__item"><div class="bar"><div class="bar__end w-100"><button expr3="expr3" class="button button--transparent"><svg class="icon fill-text-contrast" aria-hidden="true"><use xlink:href="/symbol-defs.svg#icon-delete"/></svg></button></div></div><div class="panel__body"><a expr4="expr4"><h3 expr5="expr5" class="buckets__title"> </h3><div class="content"><p expr6="expr6"> </p></div></a></div></article>',
'<article class="panel buckets__item"><div class="bar"><div class="bar__end w-100"><button expr9="expr9" class="button button--transparent"><svg class="icon fill-text-contrast" aria-hidden="true"><use xlink:href="/symbol-defs.svg#icon-delete"/></svg></button></div></div><div class="panel__body"><a expr10="expr10"><h3 expr11="expr11" class="buckets__title"> </h3><div class="content"><p expr12="expr12"> </p></div></a></div></article>',
[ [
{ {
'redundantAttribute': 'expr3',
'selector': '[expr3]',
'redundantAttribute': 'expr9',
'selector': '[expr9]',
'expressions': [ 'expressions': [
{ {
@ -102,8 +102,8 @@ __webpack_require__.r(__webpack_exports__);
] ]
}, },
{ {
'redundantAttribute': 'expr4',
'selector': '[expr4]',
'redundantAttribute': 'expr10',
'selector': '[expr10]',
'expressions': [ 'expressions': [
{ {
@ -124,8 +124,8 @@ __webpack_require__.r(__webpack_exports__);
] ]
}, },
{ {
'redundantAttribute': 'expr5',
'selector': '[expr5]',
'redundantAttribute': 'expr11',
'selector': '[expr11]',
'expressions': [ 'expressions': [
{ {
@ -145,8 +145,8 @@ __webpack_require__.r(__webpack_exports__);
] ]
}, },
{ {
'redundantAttribute': 'expr6',
'selector': '[expr6]',
'redundantAttribute': 'expr12',
'selector': '[expr12]',
'expressions': [ 'expressions': [
{ {
@ -168,8 +168,8 @@ __webpack_require__.r(__webpack_exports__);
] ]
), ),
'redundantAttribute': 'expr2',
'selector': '[expr2]',
'redundantAttribute': 'expr8',
'selector': '[expr8]',
'itemName': 'bucket', 'itemName': 'bucket',
'indexName': null, 'indexName': null,
@ -180,8 +180,8 @@ __webpack_require__.r(__webpack_exports__);
} }
}, },
{ {
'redundantAttribute': 'expr7',
'selector': '[expr7]',
'redundantAttribute': 'expr13',
'selector': '[expr13]',
'expressions': [ 'expressions': [
{ {


+ 9280
- 0
public/js/users.js
File diff suppressed because it is too large
View File


+ 86
- 0
resources/js/components/users.riot View File

@ -0,0 +1,86 @@
<app-users>
<div class="buckets">
<table class="table">
<div class="table__row" each={ user in state.users }>
<div class="table__column">
{ user.username }
</div>
<div class="table__column">
{ user.email }
</div>
<div class="table__column">
</div>
</div>
</table>
<div class="grid" if={ state.maxLength > state.users.length }>
<div class="col-12">
<div class="buckets__more">
<button type="button" class="button" onclick={ handleClick }>
More
<svg class="icon" aria-hidden="true">
<use xlink:href="/symbol-defs.svg#icon-arrow-down"></use>
</svg>
</button>
</div>
</div>
</div>
</div>
<script>
import axios from 'axios'
import remove from 'lodash.remove'
/**
*
*
*
* @author Björn Hase
*
*/
export default {
state: {
users: [],
maxLength: 0
},
onBeforeMount() {
this.fetch()
},
handleClick() {
},
handleDelete(event, bucket) {
event.preventDefault()
axios.delete('/api/bucket/' + bucket._id)
.then((response) => {
// removing from buckets
remove(this.state.buckets, function(b) {
return b._id === bucket._id
})
this.update()
})
},
/**
* getting all buckets
*
*
*/
fetch() {
axios.get('/api/users').then((response) => {
this.state.users = response.data.data
this.update()
})
}
}
</script>
</app-users>

+ 128
- 0
resources/js/components/users/form.riot View File

@ -0,0 +1,128 @@
<app-users-form>
<div class="sidebar sidebar--open">
<div class="sidebar__inner">
<div class="bar">
<div class="bar__main">
Create User
</div>
<div class="bar__end">
<button class="button button--transparent" type="button" onclick={ (event) => { handleClose(event) } }>
<svg class="icon fill-text-contrast" aria-hidden="true">
<use xlink:href="/symbol-defs.svg#icon-close"></use>
</svg>
</button>
</div>
</div>
<div class="sidebar__body">
<form id="app-users-form">
<div class="field-group">
<label class="field-label">
E-Mail
<input name="email" type="text" class="field-text" />
</label>
<field-error name="email"></field-error>
</div>
<div class="field-group">
<label class="field-label">
Password
<input name="password" type="password" class="field-text" />
</label>
<field-error name="password"></field-error>
</div>
</form>
</div>
<div class="sidebar__footer">
<button class="button m-bottom-0" type="submit" form="app-users-form">
Save
<svg class="icon fill-success p-left-3" aria-hidden="true">
<use xlink:href="/symbol-defs.svg#icon-check"></use>
</svg>
</button>
<button class="button m-bottom-0" type="submit" form="app-users-form">
Save and Close
<svg class="icon fill-success p-left-3" aria-hidden="true">
<use xlink:href="/symbol-defs.svg#icon-arrow-right"></use>
</svg>
</button>
</div>
</div>
</div>
<script>
import axios from 'axios'
import * as riot from 'riot'
import FormValidator from './../../FormValidator'
import FieldError from './../field-error.riot'
riot.register('field-error', FieldError)
riot.mount('field-error')
export default {
state: {
user: { }
},
/**
*
*
*/
onMounted(props, state) {
// create form validation
const formValidation = new FormValidator('app-users-form', {
'email': {
'length': {
'maximum': 255
},
'email': true,
'presence': true
},
'password': {
'length': {
'maximum': 64
},
'presence': true
}
}, (event, data) => {
this.handleSubmit(event, data)
})
},
handleClose(event)
{
event.preventDefault()
this.$('.sidebar').classList.remove('sidebar--open')
},
/**
*
*
*/
handleSubmit(event, data) {
let method = 'post'
if (this.state.user && this.state.user._id) {
method = 'put'
}
axios({
method: method,
url: '/api/users',
data: data
}).then((response) => {
this.state.user = response.data.data
this.update()
})
}
}
</script>
</app-users-form>

+ 11
- 0
resources/js/users.js View File

@ -0,0 +1,11 @@
import * as riot from 'riot'
import AppUsers from './components/users.riot'
import AppUsersForm from './components/users/form.riot'
// register components for buckets
riot.register('app-users', AppUsers)
riot.mount('app-users')
riot.register('app-users-form', AppUsersForm)
riot.mount('app-users-form')

+ 68
- 0
resources/scss/components/_sidebar.scss View File

@ -0,0 +1,68 @@
.sidebar {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
max-width: 33%;
z-index: 1;
visibility: hidden;
transition: visibility 0s linear 0.5s;
&__body {
@extend .panel__body;
}
&__inner {
@extend .panel;
position: relative;
height: 100%;
transition: transform 0.2s;
transform: translateX(-100%);
}
&__footer {
position: fixed;
left: 0;
bottom: 0;
display: flex;
justify-content: space-between;
background: var(--background);
width: 100%;
padding: 1rem;
}
&:before {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: transparent;
transition: background-color 0.5s;
z-index: 0;
content: "";
}
&--open {
visibility: visible;
transition: visibility 0s linear 0s;
.sidebar__inner {
transform: translateX(0);
}
&:before {
background: rgba(0,0,0,.7);
}
}
}

+ 1
- 0
resources/scss/index.scss View File

@ -4,6 +4,7 @@
'components/buckets', 'components/buckets',
'components/note-form', 'components/note-form',
'components/field', 'components/field',
'components/sidebar',
'container'; 'container';
.turbolinks-progress-bar { .turbolinks-progress-bar {


+ 3
- 0
resources/views/layout.html View File

@ -23,6 +23,9 @@
<a href="/" data-turbolinks="false" class="tabs__item tabs__item--selected"> <a href="/" data-turbolinks="false" class="tabs__item tabs__item--selected">
Dashboard Dashboard
</a> </a>
<a href="/users" class="tabs__item">
Users
</a>
<a href="/settings" class="tabs__item"> <a href="/settings" class="tabs__item">
Settings Settings
</a> </a>


+ 15
- 0
resources/views/users.html View File

@ -0,0 +1,15 @@
<% layout('./layout.html') %>
<div class="container container--app">
<div class="grid">
<div class="col-12">
<h1 class="highlight">
Users
</h1>
<app-users></app-users>
<app-users-form></app-users-form>
</div>
</div>
</div>
<script type="text/javascript" src="/js/users.js" defer></script>

+ 57
- 0
server-backup.ts View File

@ -0,0 +1,57 @@
import 'https://deno.land/x/dotenv@v2.0.0/load.ts'
import {
opine,
serveStatic,
json,
urlencoded
} from 'https://deno.land/x/opine@1.5.3/mod.ts'
import { dirname, join, createError } from "https://deno.land/x/opine@1.5.3/deps.ts";
import { renderFile } from 'https://deno.land/x/eta@v1.12.2/mod.ts'
// middleware
import session from './src/middleware/session.ts'
// getting routes
import index from './src/http/index.ts'
//import bucket from './src/http/bucket.ts'
//import settings from './src/http/settings.ts'
import users from './src/http/users.ts'
//import bucketApi from './src/http/api/bucket.ts'
//import noteApi from './src/http/api/note.ts'
const app = opine()
const __dirname = dirname(import.meta.url)
// for parsing application/json
app.use(json())
// for parsing application/x-www-form-urlencoded
app.use(urlencoded())
// adding static files
app.use(serveStatic(join(__dirname, 'public')))
// adding eta as view engine
app.engine('.html', renderFile)
app.set('views', join(__dirname, 'resources/views'))
app.set('view engine', 'html')
// adding http classes for routes
app.use('*', session)
app.use('/', index)
//app.use('/bucket', bucket)
//app.use('/settings', settings)
app.use('/users', users)
//app.use('/api/bucket', bucketApi)
//app.use('/api/note', noteApi)
app.use((request, response, next) => {
response.setStatus(404)
response.render('errors/404')
})
// let it rain
app.listen(Number(Deno.env.get('SERVER_PORT')))
console.log('running on ' + Deno.env.get('SERVER_PORT'))

+ 25
- 12
server.ts View File

@ -8,22 +8,28 @@ import {
import { dirname, join, createError } from "https://deno.land/x/opine@1.5.3/deps.ts"; import { dirname, join, createError } from "https://deno.land/x/opine@1.5.3/deps.ts";
import { renderFile } from 'https://deno.land/x/eta@v1.12.2/mod.ts' import { renderFile } from 'https://deno.land/x/eta@v1.12.2/mod.ts'
// middleware
import session from './src/middleware/session.ts'
// getting routes // getting routes
import index from './src/http/index.ts'
import bucket from './src/http/bucket.ts'
import settings from './src/http/settings.ts'
import home from './src/http/home.ts'
import buckets from './src/http/buckets.ts'
//import settings from './src/http/settings.ts'
import users from './src/http/users.ts'
import usersApi from './src/http/api/users.ts'
import bucketApi from './src/http/api/bucket.ts'
import noteApi from './src/http/api/note.ts'
//import bucketApi from './src/http/api/bucket.ts'
//import noteApi from './src/http/api/note.ts'
const app = opine() const app = opine()
const __dirname = dirname(import.meta.url) const __dirname = dirname(import.meta.url)
// for parsing application/json // for parsing application/json
app.use(json());
app.use(json())
// for parsing application/x-www-form-urlencoded // for parsing application/x-www-form-urlencoded
app.use(urlencoded());
app.use(urlencoded())
// adding static files // adding static files
app.use(serveStatic(join(__dirname, 'public'))) app.use(serveStatic(join(__dirname, 'public')))
@ -34,11 +40,18 @@ app.set('views', join(__dirname, 'resources/views'))
app.set('view engine', 'html') app.set('view engine', 'html')
// adding http classes for routes // adding http classes for routes
app.use('/', index)
app.use('/bucket', bucket)
app.use('/settings', settings)
app.use('/api/bucket', bucketApi)
app.use('/api/note', noteApi)
app.use('*', session)
app.use('/', home)
app.use('/buckets', buckets)
app.use('/users', users)
app.use('/api/users', usersApi)
//app.use('/api/bucket', bucketApi)
//app.use('/api/note', noteApi)
app.use((request, response, next) => { app.use((request, response, next) => {
response.setStatus(404) response.setStatus(404)
response.render('errors/404') response.render('errors/404')


+ 45
- 2
src/http/api/bucket.ts View File

@ -38,7 +38,10 @@ router.get('/', async function(request, response)
*/ */
router.get('/:uuid', function(request, response) router.get('/:uuid', function(request, response)
{ {
response.render('bucket/form', {
visiblities: visibilties.enums,
types: types.enums
})
}) })
/** /**
@ -50,7 +53,47 @@ router.get('/:uuid', function(request, response)
*/ */
router.post('/', function(request, response, next) router.post('/', function(request, response, next)
{ {
const body = request.body
let typeValues = <any>[]
let visiblityValues = <any>[]
types.enums.forEach(function(type) {
typeValues.push(String(type.value))
})
visibilties.enums.forEach(function(visiblity) {
visiblityValues.push(String(visiblity.value))
})
// escape before validate
if (body.title) {
body.title = escapeHtml(body.title)
}
// escape before validate
if (body.description) {
body.description = escapeHtml(body.description)
}
// validate
const [ valid, errors ] = await validate(body, {
title: [ required, maxLength(255) ],
description: [ required, maxLength(255) ],
type: [ required, isIn(typeValues) ],
visiblity: [ required, isIn(visiblityValues)]
});
if (valid) {
const db = new Database<BucketSchema>('./storage/database/buckets.json')
body._id = v4.generate()
const bucket = await db.insertOne(body)
response.redirect('/buckets/' + bucket._id)
} else {
response.redirect('/buckets/create')
}
}) })
/** /**
@ -85,7 +128,7 @@ router.delete('/:uuid', async function(request, response, next)
const db = new Database<BucketSchema>('./storage/database/buckets.json') const db = new Database<BucketSchema>('./storage/database/buckets.json')
const bucket = await db.deleteOne({ _id: request.params.uuid }); const bucket = await db.deleteOne({ _id: request.params.uuid });
// check if bucket is deleted
// check if bucket is deleted
if (bucket) { if (bucket) {
result = true result = true
} }


src/http/bucket.ts → src/http/api/buckets.ts View File

@ -1,22 +1,42 @@
import { Router } from 'https://deno.land/x/opine@1.5.3/mod.ts' import { Router } from 'https://deno.land/x/opine@1.5.3/mod.ts'
import { v4 } from "https://deno.land/std@0.99.0/uuid/mod.ts"; import { v4 } from "https://deno.land/std@0.99.0/uuid/mod.ts";
import { validate, required, isIn, maxLength } from 'https://deno.land/x/validasaur@v0.15.0/mod.ts' import { validate, required, isIn, maxLength } from 'https://deno.land/x/validasaur@v0.15.0/mod.ts'
import { Database } from 'https://deno.land/x/aloedb@0.9.0/mod.ts' import { Database } from 'https://deno.land/x/aloedb@0.9.0/mod.ts'
import { escapeHtml } from "https://deno.land/x/escape@1.3.0/mod.ts"
import { BucketSchema } from './../../stores/bucket.ts'
import { BucketSchema } from './../stores/bucket.ts'
import { visibilties, types } from './../enums/bucket.ts'
import { visibilties, types } from './../../enums/bucket.ts'
const router = Router() const router = Router()
/** /**
* render template for form
*
* *
* @param request * @param request
* @param response * @param response
* @return * @return
*/ */
router.get('/create', function(request, response, next)
router.get('/', async function(request, response)
{
const db = new Database<BucketSchema>('./storage/database/buckets.json')
const buckets = await db.findMany({
'type': request.params.visiblity
})
response.json({
data: buckets
})
})
/**
*
*
* @param request
* @param response
* @return
*/
router.get('/:uuid', function(request, response)
{ {
response.render('bucket/form', { response.render('bucket/form', {
visiblities: visibilties.enums, visiblities: visibilties.enums,
@ -25,13 +45,13 @@ router.get('/create', function(request, response, next)
}) })
/** /**
* render template for form
*
* *
* @param request * @param request
* @param response * @param response
* @return * @return
*/ */
router.post('/store', async function(request, response, next)
router.post('/', function(request, response, next)
{ {
const body = request.body const body = request.body
@ -56,7 +76,7 @@ router.post('/store', async function(request, response, next)
body.description = escapeHtml(body.description) body.description = escapeHtml(body.description)
} }
// validate
// validate
const [ valid, errors ] = await validate(body, { const [ valid, errors ] = await validate(body, {
title: [ required, maxLength(255) ], title: [ required, maxLength(255) ],
description: [ required, maxLength(255) ], description: [ required, maxLength(255) ],
@ -70,34 +90,51 @@ router.post('/store', async function(request, response, next)
body._id = v4.generate() body._id = v4.generate()
const bucket = await db.insertOne(body) const bucket = await db.insertOne(body)
response.redirect('/bucket/' + bucket._id)
response.redirect('/buckets/' + bucket._id)
} else { } else {
response.redirect('/bucket/create')
response.redirect('/buckets/create')
} }
}) })
/** /**
* render template for form
*
* *
* @param request * @param request
* @param response * @param response
* @return * @return
*/ */
router.get('/:id', async function(request, response, next)
router.put('/:uuid', function(request, response, next)
{ {
if (!v4.validate(request.params.id)) {
})
/**
* delete single bucket
*
* @param request
* @param response
* @return
*/
router.delete('/:uuid', async function(request, response, next)
{
// check if uuid is valid
if (!v4.validate(request.params.uuid)) {
response.setStatus(404) response.setStatus(404)
} }
let result = false
// getting database and search by uuid
const db = new Database<BucketSchema>('./storage/database/buckets.json') const db = new Database<BucketSchema>('./storage/database/buckets.json')
const bucket = await db.findOne({ '_id': request.params.id })
const bucket = await db.deleteOne({ _id: request.params.uuid });
if (!bucket) {
response.setStatus(404)
// check if bucket is deleted
if (bucket) {
result = true
} }
response.render('bucket/single', {
bucket: bucket
response.json({
'success': result
}) })
}) })

+ 2
- 19
src/http/api/note.ts View File

@ -19,31 +19,14 @@ const router = Router()
router.post('/:bucket_id', async function(request, response) router.post('/:bucket_id', async function(request, response)
{ {
const body = request.body const body = request.body
let note
// check if uuid is valid
if (!v4.validate(request.params.bucket_id)) {
response.setStatus(404)
}
// check if bucket exists
const db = new Database<BucketSchema>('./storage/database/buckets.json')
const bucket = await db.findOne({ _id: request.params.bucket_id })
if (!bucket) {
response.setStatus(404)
}
const [ valid, errors ] = await validate(body, { const [ valid, errors ] = await validate(body, {
title: [ maxLength(255) ], title: [ maxLength(255) ],
content: [ maxLength(10922) ] content: [ maxLength(10922) ]
}) })
if (valid && bucket) {
body._id = v4.generate()
// getting database and search by uuid
const db = new Database<NoteSchema>('./storage/database/' + bucket._id + '.json')
if (valid) {
const noteRepository = new NoteRepository(request.bucket._id)
note = await db.insertOne(body) note = await db.insertOne(body)
} }


+ 93
- 0
src/http/api/notes.ts View File

@ -0,0 +1,93 @@
import { v4 } from "https://deno.land/std@0.99.0/uuid/mod.ts";
import { validate, required, isIn, maxLength } from 'https://deno.land/x/validasaur@v0.15.0/mod.ts'
import { Database } from 'https://deno.land/x/aloedb@0.9.0/mod.ts'
import { BucketSchema } from './../../stores/bucket.ts'
import { NoteSchema } from './../../stores/note.ts'
import { Router } from 'https://deno.land/x/opine@1.5.3/mod.ts'
const router = Router()
/**
*
*
* @param request
* @param response
* @return
*/
router.post('/:bucket_id', async function(request, response)
{
const body = request.body
const [ valid, errors ] = await validate(body, {
title: [ maxLength(255) ],
content: [ maxLength(10922) ]
})
if (valid) {
const noteRepository = new NoteRepository(request.bucket._id)
note = await db.insertOne(body)
}
response.json({
data: note
})
})
/**
*
*
* @param request
* @param response
* @return
*/
router.put('/:bucket_id', async function(request, response, next)
{
const body = request.body
let note
// check if uuid is valid
if (!v4.validate(request.params.bucket_id)) {
response.setStatus(404)
}
// check if bucket exists
const bucketDb = new Database<BucketSchema>('./storage/database/buckets.json')
const bucket = await bucketDb.findOne({ _id: request.params.bucket_id })
if (!bucket) {
response.setStatus(404)
}
const [ valid, errors ] = await validate(body, {
title: [ maxLength(255) ],
content: [ maxLength(10922) ]
})
if (valid && bucket) {
// getting database and search by uuid
const db = new Database<NoteSchema>('./storage/database/' + bucket._id + '.json')
note = await db.updateOne({ _id: body._id }, body)
}
response.json({
data: note
})
})
/**
*
*
* @param request
* @param response
* @return
*/
router.delete('/:bucket_id/:id', function(request, response, next)
{
})
export default router

+ 149
- 0
src/http/api/user.ts View File

@ -0,0 +1,149 @@
import { v4 } from "https://deno.land/std@0.99.0/uuid/mod.ts";
import { validate, required, isIn, maxLength } from 'https://deno.land/x/validasaur@v0.15.0/mod.ts'
import { Database } from 'https://deno.land/x/aloedb@0.9.0/mod.ts'
import { UserSchema } from './../../stores/user.ts'
import { Router } from 'https://deno.land/x/opine@1.5.3/mod.ts'
const router = Router()
/**
* get all users
*
* @param request
* @param response
* @return
*
*/
router.get('/', async function(request, response)
{
const userRepository = new UserRepository()
const users = await userRepository.db.findAll()
response.json({
data: user
})
})
/**
* create user
*
* @param request
* @param response
* @return
*
*/
router.get('/:id', async function(request, response)
{
const userRepository = new UserRepository()
const [ valid, errors ] = await validate(request.params, {
id: [ required, uuid ]
})
if (valid) {
const user = await userRepository.db.findOne({
'_id': request.params.id
})
if (!user) {
response.setStatus(404)
}
} else {
response.setStatus(405)
}
response.json({
data: user
})
})
/**
* create user
*
* @param request
* @param response
* @return
*
*/
router.post('/', async function(request, response)
{
const body = request.body
const userRepository = new UserRepository()
const [ valid, errors ] = await validate(body, {
email: [ email, required ]
})
if (valid) {
user = await userRepository.create(body)
}
response.json({
data: user
})
})
/**
*
*
* @param request
* @param response
* @return
*/
router.put('/:id', async function(request, response, next)
{
const body = request.body
const userRepository = new UserRepository()
let user = false
const [ valid, errors ] = await validate(body, {
_id: [ required, uuid, exists ],
email: [ email, required, unique ],
password: [ maxLength(64) ],
displayname: [ maxLength(128) ],
role: [ array() ]
})
if (valid) {
user = userRepository.update(body)
}
response.json({
data: user
})
})
/**
*
*
* @param request
* @param response
* @return
*
*/
router.delete('/:id', function(request, response)
{
const userRepository = new UserRepository()
let user = false
const [ valid, errors ] = await validate(request.params, {
'_id': [ required, uuid, exists ]
})
if (valid) {
user = userRepository.db.deleteOne({
'_id': request.params._id
})
}
response.json({
data: user
})
})
export default router

+ 60
- 0
src/http/api/users.ts View File

@ -0,0 +1,60 @@
import { Router } from 'https://deno.land/x/opine@1.5.3/mod.ts'
import UserRepository from '../../repositories/user.ts'
import { validate, required, maxLength, isEmail } from 'https://deno.land/x/validasaur@v0.15.0/mod.ts'
const router = Router()
/**
* get all users
*
* @param request
* @param response
* @return
*
*/
router.get('/', async function(request, response)
{
const userRepository = new UserRepository()
const users = await userRepository.db.findMany()
response.json({
data: users
})
})
/**
* create user
*
* @param request
* @param response
* @return
*
*/
router.post('/', async function(request, response)
{
const body = request.body
const userRepository = new UserRepository()
let user = []
const [ valid, errors ] = await validate(body, {
email: [ isEmail, maxLength(255), required ],
password: [ required, maxLength(64) ]
})
if (valid) {
user = await userRepository.create(body)
// remove password
// @TODO make sure repository can hide variables
delete user.password
}
response.json({
data: user
})
})
export default router

+ 43
- 0
src/http/auth.ts View File

@ -0,0 +1,43 @@
import { validate, required, email } from 'https://deno.land/x/validasaur@v0.15.0/mod.ts'
import { Router } from 'https://deno.land/x/opine@1.5.3/mod.ts'
const router = Router()
/**
* auth user, check for password in db
* and create jwt
*
* @param request
* @param response
* @return
*/
router.post('/', async function(request, response)
{
if (body.password) {
body.password = escapeHtml(body.password)
}
const [ valid, errors ] = await validate(body, {
email: [ required, email ],
password: [ required, maxLength(64) ]
});
if (valid) {
// check if user exists
user = await userRepository.db.findOne({
'email': body.email
})
if (user) {
result = userRepository.verifyPassword(user, body.password)
if (result) {
response.cookie('auth-token', jwt)
}
}
}
response.redirect('/')
}

+ 23
- 0
src/http/buckets.ts View File

@ -0,0 +1,23 @@
import { Router } from 'https://deno.land/x/opine@1.5.3/mod.ts'
import bucketMiddleware from '../middleware/bucket.ts'
const router = Router()
// check for id and try load bucket
router.param('bucket_id', bucketMiddleware)
/**
* render single bucket
*
* @param request
* @param response
* @return
*/
router.get('/:bucket_id', async function(request, response)
{
response.render('bucket/single', {
bucket: response.locals.bucket
})
})
export default router

src/http/index.ts → src/http/home.ts View File


+ 17
- 0
src/http/users.ts View File

@ -0,0 +1,17 @@
import { Router } from 'https://deno.land/x/opine@1.5.3/mod.ts'
const router = Router()
/**
* render template for settings
*
* @param request
* @param response
* @return
*/
router.get('/', function(request, response, next)
{
response.render('users')
})
export default router

+ 51
- 0
src/middleware/bucket.ts View File

@ -0,0 +1,51 @@
import { Router } from 'https://deno.land/x/opine@1.5.3/mod.ts'
import { validate, required } from 'https://deno.land/x/validasaur@v0.15.0/mod.ts'
import { uuid } from '../rules/uuid.ts'
import BucketRepository from '../repositories/bucket.ts'
const router = Router()
/**
* check every route for single bucket
*
* @param request
* @param response
* @param next
* @return
*/
async function bucketMiddleware(request: any, response: any, next: any)
{
// max for id
request.params.bucket_id = request.params.bucket_id.slice(0, 128)
// only dash, numbers & letters are allowed
request.params.bucket_id = request.params.bucket_id.replace(/[^a-z0-9-]/gi, '')
const [ valid, errors ] = await validate(request.params, {
bucket_id: [ uuid ]
})
// if invalid send 404
if (!valid) {
response
.setStatus(404)
.send()
}
// getting
const bucketRepository = new BucketRepository()
const bucket = await bucketRepository.db.findOne({ '_id': request.params.bucket_id })
// if not exists send 404
if (!bucket) {
response
.setStatus(404)
.send()
}
response.locals.bucket = bucket
next()
}
export default bucketMiddleware

+ 32
- 0
src/middleware/session.ts View File

@ -0,0 +1,32 @@
import { Router } from 'https://deno.land/x/opine@1.5.3/mod.ts'
import { v4 } from 'https://deno.land/std@0.99.0/uuid/mod.ts'
import UserRepository from './../repositories/user.ts'
export default async function(request: any, response: any, next: any)
{
request.user = false
try {
const token = request.headers.authorization.split(' ')[1];
// if v4 is not validate next
if (!v4.validate(token)) {
next()
}
// search for user with session id
const userRepository = new UserRepository()
const user = await userRepository.db.findOne({
'session_id': token
})
if (user) {
response.locals.user = user
}
} catch(error) {
}
next()
}

+ 36
- 0
src/repositories/bucket.ts View File

@ -0,0 +1,36 @@
import { v4 } from 'https://deno.land/std@0.99.0/uuid/mod.ts'
import { Database } from 'https://deno.land/x/aloedb@0.9.0/mod.ts'
import { BucketSchema } from '../stores/bucket.ts'
/**
*
*
*/
class BucketRepository
{
db: any
constructor()
{
this.db = new Database<BucketSchema>('./storage/database/buckets.json')
}
/**
*
*/
async create(data: any)
{
data._id = v4.generate()
return await this.db.insertOne(data)
}
/**
*
*/
async update(data: any)
{
return await this.db.updateOne({ '_id': data._id }, data)
}
}
export default BucketRepository

+ 47
- 0
src/repositories/user.ts View File

@ -0,0 +1,47 @@
import * as bcrypt from 'https://deno.land/x/bcrypt@v0.2.4/mod.ts'
import { v4 } from 'https://deno.land/std@0.99.0/uuid/mod.ts'
import { Database } from 'https://deno.land/x/aloedb@0.9.0/mod.ts'
import { UserSchema } from './../stores/user.ts'
/**
*
*
*/
class UserRepository
{
db: any;
constructor()
{
this.db = new Database<UserSchema>('./../../storage/database/users.json')
}
/**
*
*/
async create(data: any)
{
data._id = v4.generate()
data.password = await bcrypt.hash(data.password)
return await this.db.insertOne(data)
}
/**
*
*/
async update(data: any)
{
const user = await this.db.findOne({ '_id': data._id })
// if password has changed hash password new
if (user && user.password !== data.password) {
data.password = await bcrypt.hash(data.password)
}
return await this.db.updateOne({ '_id': data._id }, data)
}
}
export default UserRepository

+ 0
- 0
src/respositories/user.ts View File


+ 23
- 0
src/rules/exists.ts View File

@ -0,0 +1,23 @@
import { invalid, Validity, Rule } from "https://deno.land/x/validasaur/mod.ts";
/**
*
* @param id
* @return
*/
export function exists(id: string): Rule {
return async function existsRule(value: any): Promise<Validity> {
if (typeof value !== 'string' && typeof value !== 'number') {
return invalid('exists', { value, table, column });
}
const data = await db.findOne({
key: value
})
if (data !== null) {
return invalid('unique', { value, table, column });
}
}
}

+ 25
- 0
src/rules/unique.ts View File

@ -0,0 +1,25 @@
import { invalid, Validity, Rule } from "https://deno.land/x/validasaur/mod.ts";
/**
* search for key
*
* @param key
* @param value
* @return
*/
export function unique(key: string, value: string): Rule {
return async function uniqueRule(value: any): Promise<Validity> {
if (typeof value !== 'string' && typeof value !== 'number') {
return invalid('unique', { value, table, column });
}
const data = await db.findOne({
key: value
})
if (data !== null) {
return invalid('unique', { value, table, column });
}
}
}

+ 19
- 0
src/rules/uuid.ts View File

@ -0,0 +1,19 @@
import { invalid, Validity } from 'https://deno.land/x/validasaur@v0.15.0/mod.ts'
import { v4 } from 'https://deno.land/std@0.99.0/uuid/mod.ts'
/**
* validate uuid v4
*
* @param value
* @return <Promise>
*
*/
export async function uuid(value: any): Promise<Validity> {
if (typeof value !== 'string') {
return invalid('uuid', { value: value })
}
if (!v4.validate(value)) {
return invalid('uuid', { value: value })
}
}

+ 3
- 1
src/stores/user.ts View File

@ -3,5 +3,7 @@ interface UserSchema {
email: string; email: string;
password: string; password: string;
displayname: string; displayname: string;
is_admin: boolean
session_id: string;
session_expired: string;
role: string[]
} }

+ 0
- 13
src/validators/bucket.ts View File

@ -1,13 +0,0 @@
import { validate, required, isNumber } from 'https://deno.land/x/validasaur@0.15.0/mod.ts'
// create shema for validation
const BucketSchema = Schema({
title: string.trim().normalize(),
description: string.trim().normalize().optional(),
type: Schema.either('a', 'b', 'c'),
visiblity: Schema.either('a', 'b', 'c')
})
// create type and get validator from schema
export type bucketType = Type<typeof BucketSchema>
export const bucketValidator = BucketSchema.destruct()

+ 0
- 15
src/validators/note.ts View File

@ -1,15 +0,0 @@
import Schema, { Type, string, number, array } from 'https://denoporter.sirjosh.workers.dev/v1/deno.land/x/computed_types/src/index.ts'
// create shema for validation
const NoteSchema = Schema({
title: string.trim().normalize(),
description: string.trim().normalize().optional(),
type: Schema.either('a', 'b', 'c'),
visiblity: Schema.either('a', 'b', 'c')
})
// create type and get validator from schema
type Note = Type<typeof NoteSchema>
const validator = NoteSchema.destruct()
export default validator

+ 0
- 14
src/validators/user.ts View File

@ -1,14 +0,0 @@
import Schema, { Type, string, number, array } from 'https://denoporter.sirjosh.workers.dev/v1/deno.land/x/computed_types/src/index.ts'
// create shema for validation
const UserSchema = Schema({
username: string.trim().normalize(),
password: string.normalize(),
email: Schema.email()
})
// create type and get validator from schema
type User = Type<typeof UserSchema>
const validator = UserSchema.destruct()
export default validator

+ 1
- 0
webpack.mix.js View File

@ -62,6 +62,7 @@ mix
.js('resources/js/bucket-single.js', 'public/js') .js('resources/js/bucket-single.js', 'public/js')
.js('resources/js/create-bucket.js', 'public/js') .js('resources/js/create-bucket.js', 'public/js')
.js('resources/js/dashboard.js', 'public/js') .js('resources/js/dashboard.js', 'public/js')
.js('resources/js/users.js', 'public/js')
.sass('resources/scss/index.scss', 'public/css') .sass('resources/scss/index.scss', 'public/css')
.copy( .copy(
'node_modules/@tentakelfabrik/plain-ui/src/fonts/*', 'node_modules/@tentakelfabrik/plain-ui/src/fonts/*',


Loading…
Cancel
Save