forked from platypush/platypush
Web panel improvements.
- Don't return a redirect to the login page if an authentication failed over a JSON endpoint - instead, return a JSON payload with the error. - Added support for additional fonts. - Re-designed the login/registration page. - Updated caniuse database.
This commit is contained in:
parent
c0ffea681f
commit
b9b7404230
11 changed files with 102 additions and 39 deletions
|
@ -1,6 +1,7 @@
|
|||
import json
|
||||
|
||||
from flask import Blueprint, abort, request, Response
|
||||
from flask import Blueprint, abort, request
|
||||
from flask.wrappers import Response
|
||||
|
||||
from platypush.backend.http.app import template_folder
|
||||
from platypush.backend.http.app.utils import authenticate, logger, send_message
|
||||
|
@ -14,8 +15,8 @@ __routes__ = [
|
|||
|
||||
|
||||
@execute.route('/execute', methods=['POST'])
|
||||
@authenticate()
|
||||
def execute():
|
||||
@authenticate(json=True)
|
||||
def execute_route():
|
||||
"""Endpoint to execute commands"""
|
||||
try:
|
||||
msg = json.loads(request.data.decode('utf-8'))
|
||||
|
|
|
@ -8,22 +8,27 @@ from platypush.backend.http.app import template_folder
|
|||
|
||||
|
||||
img_folder = os.path.join(template_folder, 'img')
|
||||
fonts_folder = os.path.join(template_folder, 'fonts')
|
||||
icons_folder = os.path.join(template_folder, 'icons')
|
||||
resources = Blueprint('resources', __name__, template_folder=template_folder)
|
||||
favicon = Blueprint('favicon', __name__, template_folder=template_folder)
|
||||
img = Blueprint('img', __name__, template_folder=template_folder)
|
||||
icons = Blueprint('icons', __name__, template_folder=template_folder)
|
||||
fonts = Blueprint('fonts', __name__, template_folder=template_folder)
|
||||
|
||||
# Declare routes list
|
||||
__routes__ = [
|
||||
resources,
|
||||
favicon,
|
||||
img,
|
||||
icons,
|
||||
fonts,
|
||||
]
|
||||
|
||||
|
||||
@resources.route('/resources/<path:path>', methods=['GET'])
|
||||
def resources_path(path):
|
||||
""" Custom static resources """
|
||||
"""Custom static resources"""
|
||||
path_tokens = path.split('/')
|
||||
http_conf = Config.get('backend.http')
|
||||
resource_dirs = http_conf.get('resource_dirs', {})
|
||||
|
@ -42,9 +47,11 @@ def resources_path(path):
|
|||
real_path = real_base_path
|
||||
|
||||
file_path = [
|
||||
s for s in re.sub(
|
||||
r'^{}(.*)$'.format(base_path), '\\1', path # lgtm [py/regex-injection]
|
||||
).split('/') if s
|
||||
s
|
||||
for s in re.sub(
|
||||
r'^{}(.*)$'.format(base_path), '\\1', path # lgtm [py/regex-injection]
|
||||
).split('/')
|
||||
if s
|
||||
]
|
||||
|
||||
for p in file_path[:-1]:
|
||||
|
@ -61,20 +68,26 @@ def resources_path(path):
|
|||
|
||||
@favicon.route('/favicon.ico', methods=['GET'])
|
||||
def serve_favicon():
|
||||
""" favicon.ico icon """
|
||||
"""favicon.ico icon"""
|
||||
return send_from_directory(template_folder, 'favicon.ico')
|
||||
|
||||
|
||||
@img.route('/img/<path:path>', methods=['GET'])
|
||||
def imgpath(path):
|
||||
""" Default static images """
|
||||
"""Default static images"""
|
||||
return send_from_directory(img_folder, path)
|
||||
|
||||
|
||||
@img.route('/icons/<path:path>', methods=['GET'])
|
||||
@icons.route('/icons/<path:path>', methods=['GET'])
|
||||
def iconpath(path):
|
||||
""" Default static icons """
|
||||
"""Default static icons"""
|
||||
return send_from_directory(icons_folder, path)
|
||||
|
||||
|
||||
@fonts.route('/fonts/<path:path>', methods=['GET'])
|
||||
def fontpath(path):
|
||||
"""Default fonts"""
|
||||
return send_from_directory(fonts_folder, path)
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
||||
|
|
|
@ -3,7 +3,8 @@ import logging
|
|||
import os
|
||||
|
||||
from functools import wraps
|
||||
from flask import abort, request, redirect, Response, current_app
|
||||
from flask import abort, request, redirect, jsonify, current_app
|
||||
from flask.wrappers import Response
|
||||
from redis import Redis
|
||||
|
||||
# NOTE: The HTTP service will *only* work on top of a Redis bus. The default
|
||||
|
@ -184,7 +185,25 @@ def _authenticate_csrf_token():
|
|||
)
|
||||
|
||||
|
||||
def authenticate(redirect_page='', skip_auth_methods=None, check_csrf_token=False):
|
||||
def authenticate(
|
||||
redirect_page='',
|
||||
skip_auth_methods=None,
|
||||
check_csrf_token=False,
|
||||
json=False,
|
||||
):
|
||||
def on_auth_fail():
|
||||
if json:
|
||||
return (
|
||||
jsonify(
|
||||
{
|
||||
'message': 'Not logged in',
|
||||
}
|
||||
),
|
||||
401,
|
||||
)
|
||||
|
||||
return redirect('/login?redirect=' + (redirect_page or request.url), 307)
|
||||
|
||||
def decorator(f):
|
||||
@wraps(f)
|
||||
def wrapper(*args, **kwargs):
|
||||
|
@ -213,9 +232,7 @@ def authenticate(redirect_page='', skip_auth_methods=None, check_csrf_token=Fals
|
|||
if session_auth_ok:
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return redirect(
|
||||
'/login?redirect=' + (redirect_page or request.url), 307
|
||||
)
|
||||
return on_auth_fail()
|
||||
|
||||
# CSRF token check
|
||||
if check_csrf_token:
|
||||
|
@ -224,9 +241,7 @@ def authenticate(redirect_page='', skip_auth_methods=None, check_csrf_token=Fals
|
|||
return abort(403, 'Invalid or missing csrf_token')
|
||||
|
||||
if n_users == 0 and 'session' not in skip_methods:
|
||||
return redirect(
|
||||
'/register?redirect=' + (redirect_page or request.url), 307
|
||||
)
|
||||
return on_auth_fail()
|
||||
|
||||
if (
|
||||
('http' not in skip_methods and http_auth_ok)
|
||||
|
|
12
platypush/backend/http/webapp/package-lock.json
generated
12
platypush/backend/http/webapp/package-lock.json
generated
|
@ -3705,9 +3705,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001320",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001320.tgz",
|
||||
"integrity": "sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA==",
|
||||
"version": "1.0.30001416",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001416.tgz",
|
||||
"integrity": "sha512-06wzzdAkCPZO+Qm4e/eNghZBDfVNDsCgw33T27OwBH9unE9S478OYw//Q2L7Npf/zBzs7rjZOszIFQkwQKAEqA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
@ -14544,9 +14544,9 @@
|
|||
}
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001320",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001320.tgz",
|
||||
"integrity": "sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA=="
|
||||
"version": "1.0.30001416",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001416.tgz",
|
||||
"integrity": "sha512-06wzzdAkCPZO+Qm4e/eNghZBDfVNDsCgw33T27OwBH9unE9S478OYw//Q2L7Npf/zBzs7rjZOszIFQkwQKAEqA=="
|
||||
},
|
||||
"case-sensitive-paths-webpack-plugin": {
|
||||
"version": "2.4.0",
|
||||
|
|
BIN
platypush/backend/http/webapp/public/fonts/Poppins.ttf
Normal file
BIN
platypush/backend/http/webapp/public/fonts/Poppins.ttf
Normal file
Binary file not shown.
7
platypush/backend/http/webapp/public/fonts/poppins.css
Normal file
7
platypush/backend/http/webapp/public/fonts/poppins.css
Normal file
|
@ -0,0 +1,7 @@
|
|||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(./Poppins.ttf) format('truetype');
|
||||
}
|
BIN
platypush/backend/http/webapp/public/img/logo.png
Normal file
BIN
platypush/backend/http/webapp/public/img/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
|
@ -5,6 +5,7 @@
|
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<link rel="stylesheet" href="/fonts/poppins.css">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -59,7 +59,7 @@ export default {
|
|||
},
|
||||
|
||||
async initConfig() {
|
||||
this.config = await this.request('config.get')
|
||||
this.config = await this.request('config.get', {}, 60000, false)
|
||||
this.userAuthenticated = true
|
||||
},
|
||||
},
|
||||
|
|
|
@ -4,7 +4,7 @@ import axios from 'axios'
|
|||
export default {
|
||||
name: "Api",
|
||||
methods: {
|
||||
execute(request, timeout=60000) {
|
||||
execute(request, timeout=60000, showError=true) {
|
||||
const opts = {};
|
||||
|
||||
if (!('target' in request) || !request['target']) {
|
||||
|
@ -36,22 +36,23 @@ export default {
|
|||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.notify({
|
||||
text: error,
|
||||
error: true,
|
||||
})
|
||||
if (showError)
|
||||
this.notify({
|
||||
text: error,
|
||||
error: true,
|
||||
})
|
||||
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
request(action, args={}, timeout=60000) {
|
||||
request(action, args={}, timeout=60000, showError=true) {
|
||||
return this.execute({
|
||||
type: 'request',
|
||||
action: action,
|
||||
args: args,
|
||||
}, timeout);
|
||||
}, timeout, showError);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
<template>
|
||||
<div class="login-container">
|
||||
<form class="login" method="POST">
|
||||
<div class="description">
|
||||
{{ _register ? 'Welcome' : 'Authenticate' }} to platypush
|
||||
<div class="header">
|
||||
<span class="logo" />
|
||||
<span class="text">Platypush</span>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
@ -23,7 +24,7 @@
|
|||
</label>
|
||||
</div>
|
||||
|
||||
<div class="row pull-right">
|
||||
<div class="row buttons">
|
||||
<input type="submit" class="btn btn-primary" :value="_register ? 'Register' : 'Login'">
|
||||
</div>
|
||||
|
||||
|
@ -60,7 +61,7 @@ export default {
|
|||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
body {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
@ -75,9 +76,25 @@ body {
|
|||
background: $default-bg-6;
|
||||
}
|
||||
|
||||
.description {
|
||||
.header {
|
||||
font-size: 1.2em;
|
||||
margin-bottom: 2em;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.logo {
|
||||
width: 3em;
|
||||
height: 3em;
|
||||
display: inline-flex;
|
||||
background-image: url('@/assets/img/logo.png');
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-family: Poppins, sans-serif;
|
||||
margin-left: .5em;
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
|
@ -111,6 +128,14 @@ form {
|
|||
display: flex;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
text-align: center;
|
||||
|
||||
input[type=submit] {
|
||||
padding: .5em .75em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
|
|
Loading…
Reference in a new issue