This commit is contained in:
parent
531c7acefe
commit
638e6cc435
14 changed files with 218 additions and 170 deletions
1
filadelfia_web/.npmrc
Normal file
1
filadelfia_web/.npmrc
Normal file
|
@ -0,0 +1 @@
|
|||
package-lock=false
|
|
@ -1,53 +1,73 @@
|
|||
const Authentication = require('./authentication')
|
||||
|
||||
exports.sendRequest = function(options, isPagination) {
|
||||
let token = Authentication.getToken()
|
||||
let pagination = isPagination
|
||||
|
||||
if (token) {
|
||||
options.headers = options.headers || {}
|
||||
options.headers['Authorization'] = 'Bearer ' + token
|
||||
}
|
||||
|
||||
options.extract = function(xhr) {
|
||||
if (xhr.responseText && xhr.responseText.slice(0, 9) === '<!doctype') {
|
||||
throw new Error('Expected JSON but got HTML (' + xhr.status + ': ' + this.url.split('?')[0] + ')')
|
||||
const api = {
|
||||
loading: false,
|
||||
sendRequest: function(options, isPagination) {
|
||||
api.loading = true
|
||||
let token = Authentication.getToken()
|
||||
let pagination = isPagination
|
||||
|
||||
if (token) {
|
||||
options.headers = options.headers || {}
|
||||
options.headers['Authorization'] = 'Bearer ' + token
|
||||
}
|
||||
let out = null
|
||||
if (pagination && xhr.status < 300) {
|
||||
let headers = {}
|
||||
|
||||
xhr.getAllResponseHeaders().split('\r\n').forEach(function(item) {
|
||||
var splitted = item.split(': ')
|
||||
headers[splitted[0]] = splitted[1]
|
||||
})
|
||||
|
||||
out = {
|
||||
headers: headers || {},
|
||||
data: JSON.parse(xhr.responseText),
|
||||
|
||||
options.extract = function(xhr) {
|
||||
if (xhr.responseText && xhr.responseText.slice(0, 9) === '<!doctype') {
|
||||
if (xhr.status === 500) {
|
||||
throw new Error('Server is temporarily down, try again later.')
|
||||
}
|
||||
throw new Error('Expected JSON but got HTML (' + xhr.status + ': ' + this.url.split('?')[0] + ')')
|
||||
}
|
||||
} else {
|
||||
if (xhr.responseText) {
|
||||
out = JSON.parse(xhr.responseText)
|
||||
let out = null
|
||||
if (pagination && xhr.status < 300) {
|
||||
let headers = {}
|
||||
|
||||
xhr.getAllResponseHeaders().split('\r\n').forEach(function(item) {
|
||||
var splitted = item.split(': ')
|
||||
headers[splitted[0]] = splitted[1]
|
||||
})
|
||||
|
||||
out = {
|
||||
headers: headers || {},
|
||||
data: JSON.parse(xhr.responseText),
|
||||
}
|
||||
} else {
|
||||
out = {}
|
||||
if (xhr.responseText) {
|
||||
out = JSON.parse(xhr.responseText)
|
||||
} else {
|
||||
out = {}
|
||||
}
|
||||
}
|
||||
if (xhr.status >= 300) {
|
||||
if (out.message) {
|
||||
throw out
|
||||
}
|
||||
console.error('Got error ' + xhr.status + ' but no error message:', out)
|
||||
throw new Error('Unknown or empty response from server.')
|
||||
}
|
||||
return out
|
||||
}
|
||||
if (xhr.status >= 300) {
|
||||
throw out
|
||||
}
|
||||
return out
|
||||
|
||||
return m.request(options)
|
||||
.then(function(res) {
|
||||
api.loading = false
|
||||
window.requestAnimationFrame(m.redraw.bind(m))
|
||||
return res
|
||||
})
|
||||
.catch(function (error) {
|
||||
api.loading = false
|
||||
window.requestAnimationFrame(m.redraw.bind(m))
|
||||
if (error.status === 403) {
|
||||
Authentication.clearToken()
|
||||
m.route.set('/', { redirect: m.route.get() })
|
||||
}
|
||||
if (error.response && error.response.status) {
|
||||
return Promise.reject(error.response)
|
||||
}
|
||||
return Promise.reject(error)
|
||||
})
|
||||
}
|
||||
|
||||
return m.request(options)
|
||||
.catch(function (error) {
|
||||
if (error.status === 403) {
|
||||
Authentication.clearToken()
|
||||
m.route.set('/', { redirect: m.route.get() })
|
||||
}
|
||||
if (error.response && error.response.status) {
|
||||
return Promise.reject(error.response)
|
||||
}
|
||||
return Promise.reject(error)
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = api
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
const m = require('mithril')
|
||||
const storageName = 'nfp_sites_filadelfia_web_logintoken'
|
||||
|
||||
const Authentication = {
|
||||
|
@ -33,6 +34,16 @@ const Authentication = {
|
|||
getToken: function() {
|
||||
return localStorage.getItem(storageName)
|
||||
},
|
||||
|
||||
requiresLogin: function() {
|
||||
if (Authentication.currentUser) return
|
||||
m.route.set('/')
|
||||
},
|
||||
|
||||
requiresNotLogin: function() {
|
||||
if (!Authentication.currentUser) return
|
||||
m.route.set('/browse')
|
||||
},
|
||||
}
|
||||
|
||||
Authentication.updateToken(localStorage.getItem(storageName))
|
||||
|
|
|
@ -23,7 +23,6 @@ const Menu = {
|
|||
},
|
||||
|
||||
view: function() {
|
||||
console.log(Authentication.currentUser)
|
||||
return Authentication.currentUser
|
||||
? [
|
||||
m('nav', [
|
||||
|
|
|
@ -3,6 +3,7 @@ const Authentication = require('./authentication')
|
|||
const Header = require('./header')
|
||||
const Login = require('./page_login')
|
||||
const Browse = require('./page_browse')
|
||||
const Upload = require('./page_upload')
|
||||
window.m = m
|
||||
|
||||
var fileref = document.createElement("link");
|
||||
|
@ -28,6 +29,7 @@ m.route.prefix = ''
|
|||
const allRoutes = {
|
||||
'/': Login,
|
||||
'/browse': Browse,
|
||||
'/upload': Upload,
|
||||
}
|
||||
|
||||
// Wait until we finish checking avif support, some views render immediately and will ask for this immediately before the callback gets called.
|
||||
|
|
19
filadelfia_web/app/input.js
Normal file
19
filadelfia_web/app/input.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
const m = require('mithril')
|
||||
|
||||
const Input = {
|
||||
oninit: function(vnode) {
|
||||
},
|
||||
|
||||
view: function(vnode) {
|
||||
return [
|
||||
m('label', vnode.attrs.label),
|
||||
m('input', {
|
||||
type: vnode.attrs.type || 'text',
|
||||
value: vnode.attrs.form[vnode.attrs.formKey],
|
||||
oninput: (e) => { vnode.attrs.form[vnode.attrs.formKey] = e.currentTarget.value },
|
||||
}),
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = Input
|
|
@ -4,15 +4,12 @@ const videos = require('./videos')
|
|||
|
||||
const Browse = {
|
||||
oninit: function(vnode) {
|
||||
Authentication.requiresLogin()
|
||||
if (!videos.Tree.length) {
|
||||
this.refreshTree()
|
||||
}
|
||||
},
|
||||
|
||||
oncreate: function() {
|
||||
if (Authentication.currentUser) return
|
||||
},
|
||||
|
||||
refreshTree: function(vnode) {
|
||||
videos.refreshTree()
|
||||
},
|
||||
|
|
|
@ -1,55 +1,42 @@
|
|||
const m = require('mithril')
|
||||
const Authentication = require('./authentication')
|
||||
const api = require('./api')
|
||||
const Input = require('./input')
|
||||
|
||||
const Login = {
|
||||
oninit: function(vnode) {
|
||||
this.redirect = vnode.attrs.redirect || ''
|
||||
if (Authentication.currentUser) return m.route.set('/browse')
|
||||
Authentication.requiresNotLogin()
|
||||
|
||||
this.error = ''
|
||||
this.loading = false
|
||||
this.username = ''
|
||||
this.password = ''
|
||||
},
|
||||
|
||||
oncreate: function() {
|
||||
if (Authentication.currentUser) return
|
||||
this.form = {
|
||||
email: '',
|
||||
password: '',
|
||||
}
|
||||
},
|
||||
|
||||
loginuser: function(vnode, e) {
|
||||
e.preventDefault()
|
||||
this.error = ''
|
||||
|
||||
if (!this.password) this.error = 'Password is missing'
|
||||
if (!this.username) this.error = 'Email is missing'
|
||||
if (!this.form.password) this.error = 'Password is missing'
|
||||
if (!this.form.email) this.error = 'Email is missing'
|
||||
|
||||
if (this.error) return false
|
||||
|
||||
this.loading = true
|
||||
|
||||
api.sendRequest({
|
||||
method: 'POST',
|
||||
url: '/api/authentication/login',
|
||||
body: {
|
||||
email: this.username,
|
||||
password: this.password,
|
||||
},
|
||||
body: this.form,
|
||||
})
|
||||
.then((result) => {
|
||||
if (!result.token) {
|
||||
return Promise.reject(new Error('Server authentication down.'))
|
||||
}
|
||||
if (!result.token) return Promise.reject(new Error('Server authentication down.'))
|
||||
Authentication.updateToken(result.token)
|
||||
m.route.set(this.redirect || '/browse')
|
||||
})
|
||||
.catch((error) => {
|
||||
this.error = 'Error while logging in! ' + error.message
|
||||
vnode.state.password = ''
|
||||
})
|
||||
.then(() => {
|
||||
this.loading = false
|
||||
m.redraw()
|
||||
this.error = 'Error while logging in: ' + error.message
|
||||
this.form.password = ''
|
||||
})
|
||||
|
||||
return false
|
||||
|
@ -57,32 +44,39 @@ const Login = {
|
|||
|
||||
view: function(vnode) {
|
||||
return [
|
||||
m('div.modal', [
|
||||
m('form', {
|
||||
onsubmit: this.loginuser.bind(this, vnode),
|
||||
}, [
|
||||
m('h3', 'Filadelfia archival center'),
|
||||
this.error ? m('p.error', this.error) : null,
|
||||
m('label', 'Email or name'),
|
||||
m('input', {
|
||||
type: 'text',
|
||||
value: this.username,
|
||||
oninput: (e) => { this.username = e.currentTarget.value },
|
||||
}),
|
||||
m('label', 'Password'),
|
||||
m('input', {
|
||||
type: 'password',
|
||||
value: this.password,
|
||||
oninput: (e) => { this.password = e.currentTarget.value },
|
||||
}),
|
||||
m('input.spinner', {
|
||||
hidden: this.loading,
|
||||
type: 'submit',
|
||||
value: 'Log in',
|
||||
}),
|
||||
this.loading ? m('div.loading-spinner') : null,
|
||||
m('div.page.page-login', [
|
||||
m('div.modal', [
|
||||
m('form', {
|
||||
onsubmit: this.loginuser.bind(this, vnode),
|
||||
}, [
|
||||
m('h3', 'Filadelfia archival center'),
|
||||
this.error ? m('p.error', this.error) : null,
|
||||
m(Input, {
|
||||
label: 'Email or name',
|
||||
form: this.form,
|
||||
formKey: 'email',
|
||||
}),
|
||||
m(Input, {
|
||||
label: 'Password',
|
||||
type: 'password',
|
||||
form: this.form,
|
||||
formKey: 'password',
|
||||
}),
|
||||
m('input.spinner', {
|
||||
hidden: api.loading,
|
||||
type: 'submit',
|
||||
value: 'Log in',
|
||||
}),
|
||||
api.loading ? m('div.loading-spinner') : null,
|
||||
]),
|
||||
m('div.login--asuna.spritesheet'),
|
||||
]),
|
||||
m('footer', [
|
||||
'Photo by ',
|
||||
m('a', { href: 'https://unsplash.com/@franhotchin?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash', target: '_blank' }, 'Francesca Hotchin'),
|
||||
' on ',
|
||||
m('a', { href: 'https://unsplash.com/photos/landscape-photo-of-mountain-covered-with-snow-FN-cedy6NHA?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash', target: '_blank' }, 'Unsplash'),
|
||||
]),
|
||||
m('div.login--asuna.spritesheet'),
|
||||
]),
|
||||
]
|
||||
},
|
||||
|
|
|
@ -1,91 +1,54 @@
|
|||
const m = require('mithril')
|
||||
const Authentication = require('./authentication')
|
||||
const api = require('./api')
|
||||
const Input = require('./input')
|
||||
|
||||
const Login = {
|
||||
const Upload = {
|
||||
oninit: function(vnode) {
|
||||
this.redirect = vnode.attrs.redirect || ''
|
||||
if (!Authentication.currentUser) return m.route.set('/')
|
||||
|
||||
Authentication.requiresLogin()
|
||||
this.error = ''
|
||||
this.loading = false
|
||||
this.body = {
|
||||
year: new Date().getFullYear(),
|
||||
month: new Date().getMonth() + 1,
|
||||
this.form = {
|
||||
title: '',
|
||||
date: new Date(),
|
||||
file: null,
|
||||
}
|
||||
},
|
||||
|
||||
uploadfile: function(vnode, e) {
|
||||
e.preventDefault()
|
||||
this.error = ''
|
||||
|
||||
if (!this.password) this.error = 'Password is missing'
|
||||
if (!this.username) this.error = 'Email is missing'
|
||||
|
||||
if (this.error) return false
|
||||
|
||||
this.loading = true
|
||||
|
||||
api.sendRequest({
|
||||
method: 'POST',
|
||||
url: '/api/authentication/login',
|
||||
body: {
|
||||
email: this.username,
|
||||
password: this.password,
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
if (!result.token) {
|
||||
return Promise.reject(new Error('Server authentication down.'))
|
||||
}
|
||||
Authentication.updateToken(result.token)
|
||||
m.route.set(this.redirect || '/browse')
|
||||
})
|
||||
.catch((error) => {
|
||||
this.error = 'Error while logging in! ' + error.message
|
||||
vnode.state.password = ''
|
||||
})
|
||||
.then(() => {
|
||||
this.loading = false
|
||||
m.redraw()
|
||||
})
|
||||
|
||||
return false
|
||||
|
||||
uploadvideo: function(vnode, e) {
|
||||
},
|
||||
|
||||
view: function(vnode) {
|
||||
return [
|
||||
m('div.modal', [
|
||||
m('form', {
|
||||
onsubmit: this.uploadfile.bind(this, vnode),
|
||||
}, [
|
||||
m('h3', 'Filadelfia archival center'),
|
||||
this.error ? m('p.error', this.error) : null,
|
||||
m('label', 'Email or name'),
|
||||
m('input', {
|
||||
type: 'text',
|
||||
value: this.username,
|
||||
oninput: (e) => { this.username = e.currentTarget.value },
|
||||
}),
|
||||
m('label', 'Password'),
|
||||
m('input', {
|
||||
type: 'password',
|
||||
value: this.password,
|
||||
oninput: (e) => { this.password = e.currentTarget.value },
|
||||
}),
|
||||
m('input.spinner', {
|
||||
hidden: this.loading,
|
||||
type: 'submit',
|
||||
value: 'Log in',
|
||||
}),
|
||||
this.loading ? m('div.loading-spinner') : null,
|
||||
m('div.page.page-upload', [
|
||||
m('div.modal', [
|
||||
m('form', {
|
||||
onsubmit: this.uploadvideo.bind(this, vnode),
|
||||
}, [
|
||||
m('h3', 'Upload new video'),
|
||||
this.error ? m('p.error', this.error) : null,
|
||||
m(Input, {
|
||||
label: 'Title',
|
||||
form: this.form,
|
||||
formKey: 'title',
|
||||
}),
|
||||
m(Input, {
|
||||
label: 'Date',
|
||||
type: 'datetime',
|
||||
form: this.form,
|
||||
formKey: 'date',
|
||||
}),
|
||||
m('input.spinner', {
|
||||
hidden: api.loading,
|
||||
type: 'submit',
|
||||
value: 'Log in',
|
||||
}),
|
||||
api.loading ? m('div.loading-spinner') : null,
|
||||
]),
|
||||
m('div.login--asuna.spritesheet'),
|
||||
]),
|
||||
m('div.login--asuna.spritesheet'),
|
||||
]),
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = Login
|
||||
module.exports = Upload
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
"build": "asbundle app/index.js public/assets/app.js",
|
||||
"dev:build": "eltro --watch build --npm build",
|
||||
"dev:server": "eltro --watch server --npm server",
|
||||
"dev:build:old": "npm-watch build",
|
||||
"dev:server:old": "npm-watch server",
|
||||
"server": "node dev.mjs | bunyan"
|
||||
},
|
||||
"watch": {
|
||||
|
@ -52,6 +54,8 @@
|
|||
"nconf-lite": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eonasdan/tempus-dominus": "^6.7.19",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"asbundle": "^2.6.1",
|
||||
"eltro": "^1.4.4",
|
||||
"flaska": "^1.3.2",
|
||||
|
|
Binary file not shown.
BIN
filadelfia_web/public/assets/bg.jpg
Normal file
BIN
filadelfia_web/public/assets/bg.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 60 KiB |
|
@ -13,6 +13,7 @@
|
|||
:root {
|
||||
--bg: #fff;
|
||||
--bg-component: #f3f7ff;
|
||||
--bg-component-half: #f3f7ff77;
|
||||
--bg-component-alt: #ffd99c;
|
||||
--color: #031131;
|
||||
--main: #1066ff;
|
||||
|
@ -121,6 +122,12 @@ h1 {
|
|||
flex-direction: column;
|
||||
}
|
||||
|
||||
.page {
|
||||
flex: 2 1 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.modal {
|
||||
flex: 2 1 auto;
|
||||
display: flex;
|
||||
|
@ -236,6 +243,37 @@ form p, label {
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
footer a {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
/* login */
|
||||
|
||||
.page-login {
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.page-login .modal form {
|
||||
backdrop-filter: blur(10px);
|
||||
background: var(--bg-component-half);
|
||||
}
|
||||
|
||||
.avifsupport .page-login {
|
||||
background-image: url('./assets/bg.avif');
|
||||
}
|
||||
|
||||
.jpegonly .page-login {
|
||||
background-image: url('./assets/bg.jpg');
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
BIN
filadelfia_web/rawassets/bg.png
Normal file
BIN
filadelfia_web/rawassets/bg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 MiB |
Loading…
Reference in a new issue