From 53a5daea6ec1d3e868974fe4c132bdebbbe8a943 Mon Sep 17 00:00:00 2001 From: Moritz Schneider <scmoritz@student.ethz.ch> Date: Sun, 29 Oct 2017 18:02:12 +0100 Subject: [PATCH] Added login --- .eslintrc.js | 1 + src/index.js | 10 +++- src/layout.js | 16 ------ src/models/auth.js | 106 ++++++++++++++++++++++++++++++++++++++++ src/models/config.js | 7 +++ src/models/log.js | 9 ++++ src/{ => views}/amiv.js | 0 src/views/layout.js | 37 ++++++++++++++ src/views/login.js | 31 ++++++++++++ 9 files changed, 199 insertions(+), 18 deletions(-) delete mode 100644 src/layout.js create mode 100644 src/models/auth.js create mode 100644 src/models/config.js create mode 100644 src/models/log.js rename src/{ => views}/amiv.js (100%) create mode 100644 src/views/layout.js create mode 100644 src/views/login.js diff --git a/.eslintrc.js b/.eslintrc.js index 2a3a7f3b..4c4cab19 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -7,5 +7,6 @@ module.exports = { "rules": { "no-multi-str": 0, "no-underscore-dangle": 0, + "no-console": 0 }, }; \ No newline at end of file diff --git a/src/index.js b/src/index.js index 80b1d588..38feb4a3 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,8 @@ // src/index.js const m = require('mithril'); -const Layout = require('./layout'); -const amiv = require('./amiv'); +const Layout = require('./views/layout'); +const amiv = require('./views/amiv'); +const login = require('./views/login'); m.route(document.body, '/', { '/': { @@ -9,4 +10,9 @@ m.route(document.body, '/', { return m(Layout, m(amiv)); }, }, + '/login': { + render() { + return m(Layout, m(login)); + }, + }, }); diff --git a/src/layout.js b/src/layout.js deleted file mode 100644 index c6ba9c15..00000000 --- a/src/layout.js +++ /dev/null @@ -1,16 +0,0 @@ -const m = require('mithril'); - -module.exports = { - view(vnode) { - return m('div', [ - m('nav', [ - m('a', { href: '/' }, 'AMIV'), - m('a', { href: '/events' }, 'Events'), - m('a', { href: '/studydocuments' }, 'Studienunterlagen'), - m('a', { href: '/jobs' }, 'Jobs'), - m('a', { href: '/profile' }, 'Profil'), - ]), - m('main', vnode.children), - ]); - }, -}; diff --git a/src/models/auth.js b/src/models/auth.js new file mode 100644 index 00000000..198dde98 --- /dev/null +++ b/src/models/auth.js @@ -0,0 +1,106 @@ +const m = require('mithril'); +const Config = require('./config'); +const log = require('./log'); + +const auth = { + username: '', + token: '', + etag: '', + error: '', + id: '', + authenticated: false, + lastChecked: 0, + login(username, password) { + this.reloadLocalStorage(); + return m.request({ + method: 'POST', + url: `${Config.api_url}/sessions`, + data: { username, password }, + }).then((result) => { + const dt = new Date(); + log.log('logged in!'); + this.token = result.token; + this.etag = result._etag; + this.id = result._id; + this.authenticated = true; + this.username = username; + localStorage.setItem('token', result.token); + localStorage.setItem('username', username); + localStorage.setItem('id', result._id); + localStorage.setItem('etag', result._etag); + this.lastChecked = dt.getTime(); + m.route.set('/'); + }).catch((e) => { + this.error = e.message; + }); + }, + logout() { + this.reloadLocalStorage(); + this.authenticated = false; + return m.request({ + method: 'DELETE', + url: `${Config.api_url}/sessions/${this.id}`, + headers: { + Authorization: `Token ${this.token}`, + 'If-Match': this.etag, + }, + }).then(() => { + log.log('logged out!'); + this.token = ''; + this.authenticated = false; + this.error = ''; + localStorage.removeItem('token'); + localStorage.removeItem('username'); + localStorage.removeItem('id'); + localStorage.removeItem('etag'); + m.route.set('/login'); + }).catch((e) => { + this.error = e.message; + this.authenticated = false; + m.route.set('/login'); + }); + }, + checkLogin() { + const dt = new Date(); + auth.reloadLocalStorage(); + if (this.authenticated === true) { + log.log('no session found'); + m.route.set('/login'); + return new Promise(() => { }); + } + if (dt.getTime() > this.lastChecked + 5000) { + return m.request({ + method: 'GET', + url: `${Config.api_url}/sessions/${this.token}`, + }).then((result) => { + const dt2 = new Date(); + log.log('session is still valid!'); + this.authenticated = true; + this.etag = result._etag; + this.lastChecked = dt2.getTime(); + }).catch((e) => { + log.log('token is not valid'); + log.log(e); + this.authenticated = false; + localStorage.removeItem('session'); + localStorage.removeItem('username'); + localStorage.removeItem('id'); + localStorage.removeItem('etag'); + m.route.set('/login'); + }); + } + return new Promise(() => { }); + }, + reloadLocalStorage() { + log.log('checking stored session'); + if (localStorage.getItem('token') !== null) { + this.token = localStorage.token; + this.id = localStorage.id; + this.username = localStorage.username; + this.etag = localStorage.etag; + this.authenticated = true; + } + }, +}; + +module.exports = auth; diff --git a/src/models/config.js b/src/models/config.js new file mode 100644 index 00000000..a607d5e3 --- /dev/null +++ b/src/models/config.js @@ -0,0 +1,7 @@ + +const Config = { + api_url: 'https://amiv-api.ethz.ch', + verbose: true, +}; + +module.exports = Config; diff --git a/src/models/log.js b/src/models/log.js new file mode 100644 index 00000000..d9e3b19c --- /dev/null +++ b/src/models/log.js @@ -0,0 +1,9 @@ +const config = require('./config.js'); + +const log = { + log(message) { + if (config.verbose === true) console.log(message); + }, +}; + +module.exports = log; diff --git a/src/amiv.js b/src/views/amiv.js similarity index 100% rename from src/amiv.js rename to src/views/amiv.js diff --git a/src/views/layout.js b/src/views/layout.js new file mode 100644 index 00000000..50c9acba --- /dev/null +++ b/src/views/layout.js @@ -0,0 +1,37 @@ +const m = require('mithril'); +const auth = require('../models/auth'); + +module.exports = { + oninit: auth.checkLogin, + view(vnode) { + if (auth.authenticated === false) { + return m('div', [ + m('nav', [ + m('a', { href: '/' }, 'AMIV'), + m('a', { href: '/#!/events' }, 'Events'), + m('a', { href: '/#!/studydocuments' }, 'Studienunterlagen'), + m('a', { href: '/#!/jobs' }, 'Jobs'), + m('a', { href: '/#!/login' }, 'Login'), + ]), + m('main', vnode.children), + ]); + } + return m('div', [ + m('nav', [ + m('a', { href: '/' }, 'AMIV'), + m('a', { href: '/#!/events' }, 'Events'), + m('a', { href: '/#!/studydocuments' }, 'Studienunterlagen'), + m('a', { href: '/#!/jobs' }, 'Jobs'), + m('a', { href: '/#!/profile' }, 'Profil'), + m('a', { + href: '#', + onclick: () => { + auth.logout(); + return false; + }, + }, 'Logout'), + ]), + m('main', vnode.children), + ]); + }, +}; diff --git a/src/views/login.js b/src/views/login.js new file mode 100644 index 00000000..e1fd04b4 --- /dev/null +++ b/src/views/login.js @@ -0,0 +1,31 @@ +const m = require('mithril'); +const Auth = require('../models/auth'); + +module.exports = { + username: '', + password: '', + view() { + return m('div', [ + m( + 'form', { + onsubmit: (e) => { + e.preventDefault(); + Auth.login(this.username, this.password); + }, + }, + m('h3', 'Login'), [ + m('p', Auth.error), + m('input.input[type=text][placeholder=Username]', { + oninput: m.withAttr('value', (value) => { this.username = value; }), + value: this.username, + }), + m('input.input[placeholder=Password][type=password]', { + oninput: m.withAttr('value', (value) => { this.password = value; }), + value: this.password, + }), + m('button.button[type=submit]', 'Login'), + ], + ), + ]); + }, +}; -- GitLab