misc: several improvements on the web ui

This commit is contained in:
Simone Margaritelli 2019-11-13 14:37:13 +01:00
parent 6e26463278
commit b1d8aa3ba1
14 changed files with 304 additions and 405 deletions

View File

@ -32,7 +32,10 @@ if __name__ == '__main__':
help="Enable debug logs.")
parser.add_argument('--version', dest="version", action="store_true", default=False,
help="Prints the version.")
help="Print the version.")
parser.add_argument('--print-config', dest="print_config", action="store_true", default=False,
help="Print the configuration.")
args = parser.parse_args()
@ -41,6 +44,10 @@ if __name__ == '__main__':
exit(0)
config = utils.load_config(args)
if args.print_config:
print(yaml.dump(config, default_flow_style=False))
exit(0)
utils.setup_logging(args, config)
pwnagotchi.set_name(config['main']['name'])
@ -53,8 +60,6 @@ if __name__ == '__main__':
logging.info("%s@%s (v%s)" % (pwnagotchi.name(), agent.fingerprint(), pwnagotchi.version))
logging.debug("effective configuration:\n\n%s\n\n" % yaml.dump(config, default_flow_style=False))
for _, plugin in plugins.loaded.items():
logging.debug("plugin '%s' v%s" % (plugin.__class__.__name__, plugin.__version__))

View File

@ -12,7 +12,11 @@ API_ADDRESS = "http://127.0.0.1:8666/api/v1"
def is_connected():
try:
socket.create_connection(("api.pwnagotchi.ai", 443), timeout=30)
# check DNS
host = socket.gethostbyname('api.pwnagotchi.ai')
if host:
# check connectivity itself
socket.create_connection((host, 443), timeout=30)
return True
except:
pass

View File

@ -119,12 +119,14 @@ class View(object):
def _refresh_handler(self):
delay = 1.0 / self._config['ui']['fps']
# logging.info("view refresh handler started with period of %.2fs" % delay)
while True:
try:
name = self._state.get('name')
self.set('name', name.rstrip('').strip() if '' in name else (name + ''))
self.update()
except Exception as e:
logging.warning("non fatal error while updating view: %s" % e)
time.sleep(delay)
def set(self, key, value):
@ -360,14 +362,15 @@ class View(object):
if self._frozen:
return
changes = self._state.changes(ignore=self._ignore_changes)
state = self._state
changes = state.changes(ignore=self._ignore_changes)
if force or len(changes):
self._canvas = Image.new('1', (self._width, self._height), WHITE)
drawer = ImageDraw.Draw(self._canvas)
plugins.on('ui_update', self)
for key, lv in self._state.items():
for key, lv in state.items():
lv.draw(self._canvas, drawer)
web.update_frame(self._canvas)

View File

@ -3,6 +3,7 @@ import os
import base64
import _thread
import secrets
import json
from functools import wraps
# https://stackoverflow.com/questions/14888799/disable-console-messages-in-flask-server
@ -61,9 +62,11 @@ class Handler:
@wraps(f)
def wrapper(*args, **kwargs):
auth = request.authorization
if not auth or not auth.username or not auth.password or not self._check_creds(auth.username, auth.password):
if not auth or not auth.username or not auth.password or not self._check_creds(auth.username,
auth.password):
return Response('Unauthorized', 401, {'WWW-Authenticate': 'Basic realm="Unauthorized"'})
return f(*args, **kwargs)
return wrapper
def index(self):
@ -82,6 +85,9 @@ class Handler:
error = None
try:
if not grid.is_connected():
raise Exception('not connected')
inbox = grid.inbox(page, with_pager=True)
except Exception as e:
logging.exception('error while reading pwnmail inbox')
@ -106,7 +112,7 @@ class Handler:
return render_template('profile.html',
name=pwnagotchi.name(),
fingerprint=self._agent.fingerprint(),
data=data,
data=json.dumps(data, indent=2),
error=error)
def inbox_peers(self):
@ -129,6 +135,9 @@ class Handler:
error = None
try:
if not grid.is_connected():
raise Exception('not connected')
message = grid.inbox_message(id)
if message['data']:
message['data'] = base64.b64decode(message['data']).decode("utf-8")
@ -151,6 +160,9 @@ class Handler:
error = None
try:
if not grid.is_connected():
raise Exception('not connected')
grid.send_message(to, message)
except Exception as e:
error = str(e)
@ -158,6 +170,9 @@ class Handler:
return jsonify({"error": error})
def mark_message(self, id, mark):
if not grid.is_connected():
abort(200)
logging.info("marking message %d as %s" % (int(id), mark))
grid.mark_message(id, mark)
return redirect("/inbox")

View File

@ -35,6 +35,7 @@ class Server:
static_url_path='',
static_folder=os.path.join(web_path, 'static'),
template_folder=os.path.join(web_path, 'templates'))
app.secret_key = secrets.token_urlsafe(256)
if self._origin:

View File

@ -1,22 +1,7 @@
form {
margin-block-end: 0;
.ui-image {
width: 100%;
}
.content {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
height: calc(var(--vh, 1vh) * 100);
display: flex;
flex-direction: column;
}
/**
* make sure image is displayed without any blur
*/
.pixelated {
image-rendering: optimizeSpeed; /* Legal fallback */
image-rendering: -moz-crisp-edges; /* Firefox */
@ -33,33 +18,6 @@ form {
position: relative;
}
.ui-image {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
max-width: 100vw;
height: 100%;
object-fit: contain;
object-position: 50% 0;
}
.buttons-wrapper {
flex-shrink: 0;
display: flex;
flex-wrap: wrap;
padding: 0 16px;
}
.button {
border: 1px solid black;
border-radius: 4px;
font-size: 2em;
background: #f8b506;
margin: 16px;
}
div.status {
position: absolute;
top: 0;

View File

@ -1,7 +0,0 @@
window.onload = function() {
var image = document.getElementById("ui");
function updateImage() {
image.src = image.src.split("?")[0] + "?" + new Date().getTime();
}
setInterval(updateImage, 1000);
}

View File

@ -0,0 +1,71 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>
{% block title %}
{% endblock %}
</title>
<link rel="stylesheet" href="/js/jquery.mobile/jquery.mobile-1.4.5.min.css"/>
<script type="text/javascript" src="/js/jquery-1.12.4.min.js"></script>
<script type="text/javascript" src="/js/jquery.mobile/jquery.mobile-1.4.5.min.js"></script>
<script type="text/javascript" src="/js/jquery.timeago.js"></script>
<script type="text/javascript" src="/js/jquery-qrcode-0.17.0.min.js"></script>
<link rel="stylesheet" type="text/css" href="/css/style.css"/>
<script type="text/javascript">
$.mobile.ajaxEnabled = false;
jQuery(document).ready(function() {
jQuery("time.timeago").timeago();
});
{% block script %}
{% endblock %}
</script>
</head>
<body>
<div data-role="page">
{% if error %}
<div id="error" class="error ui-content" data-role="popup" data-overlay-theme="a" data-theme="b">
<p>{{ error }}</p>
</div>
<script>
$(function(){
$("#error").popup("open");
});
</script>
{% endif %}
{% set navigation = [
( '/', 'home', 'eye', 'Home' ),
( '/inbox', 'inbox', 'bars', 'Inbox' ),
( '/inbox/new', 'new', 'mail', 'New' ),
( '/inbox/profile', 'profile', 'info', 'Profile' ),
( '/inbox/peers', 'peers', 'user', 'Peers' ),
] %}
{% set active_page = active_page|default('inbox') %}
<div data-role="footer">
<div data-role="navbar" data-iconpos="left">
<ul>
{% for href, id, icon, caption in navigation %}
<li>
<a href="{{ href }}" id="{{ id }}" data-icon="{{ icon }}" class="{{ 'ui-btn-active' if active_page == id }}">{{ caption }}</a>
</li>
{% endfor %}
</ul>
</div>
</div>
{% block content %}
{% endblock %}
</div>
</body>
</html>

View File

@ -1,48 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
{% extends "base.html" %}
{% set active_page = "inbox" %}
<title>{{ name }} Inbox</title>
{% block title %}
{{ name }} Inbox
{% endblock %}
<link rel="stylesheet" href="/js/jquery.mobile/jquery.mobile-1.4.5.min.css"/>
<script type="text/javascript" src="/js/jquery-1.12.4.min.js"></script>
<script type="text/javascript" src="/js/jquery.mobile/jquery.mobile-1.4.5.min.js"></script>
<script type="text/javascript" src="/js/jquery.timeago.js"></script>
<link rel="stylesheet" type="text/css" href="/css/style.css"/>
<script type="text/javascript">
$.mobile.ajaxEnabled = false;
jQuery(document).ready(function() {
jQuery("time.timeago").timeago();
});
</script>
</head>
<body>
<div data-role="page">
{% if error %}
<div class="error">{{ error }}</div>
{% else %}
{% if inbox.records == 0 %}
<small>Inbox is empty.</small>
{% else %}
<div data-role="footer">
<div data-role="navbar">
<ul>
<li><a href="/inbox" id="email" class="ui-btn-active" data-icon="bars">Inbox</a></li>
<li><a href="/inbox/new" id="new" data-icon="mail">New</a></li>
<li><a href="/inbox/profile" id="profile" data-icon="info">Profile</a></li>
<li><a href="/inbox/peers" id="peers" data-icon="user">Peers</a></li>
</ul>
</div>
</div>
<ul class="inbox" data-role="listview" data-filter="true" data-filter-placeholder="Search inbox..." data-inset="true">
{% block content %}
<ul class="inbox" data-role="listview" data-filter="true" data-filter-placeholder="Search inbox..." data-inset="true">
{% for message in inbox.messages %}
<li class="message">
<a href="/inbox/{{ message.id }}" class="{{ 'unread' if not message.seen_at else 'read' }}">
@ -57,11 +21,10 @@
</a>
</li>
{% endfor %}
</ul>
{% endif %}
</ul>
{% if inbox.pages > 1 %}
<div data-role="navbar">
{% if inbox.pages > 1 %}
<div data-role="navbar">
<ul>
{% if page > 1 %}
<li><a href="/inbox?p={{ page - 1 }}" class="ui-btn">Prev</a></li>
@ -70,12 +33,6 @@
<li><a href="/inbox?p={{ page + 1 }}" class="ui-btn">Next</a></li>
{% endif %}
</ul>
</div>
{% endif %}
{% endif %}
</div>
</body>
</html>
{% endif %}
{% endblock %}

View File

@ -1,35 +1,39 @@
<html>
<head>
<title>{{ title }}</title>
<link rel="stylesheet" type="text/css" href="/css/style.css"/>
</head>
<body>
<div class="content">
<div class="image-wrapper">
<img class="ui-image pixelated" src="/ui" id="ui"/>
</div>
<div class="buttons-wrapper">
<form class="action" method="POST" action="/shutdown"
onsubmit="return confirm('This will halt the unit, continue?');">
<input type="submit" class="button" value="Shutdown"/>
{% extends "base.html" %}
{% set active_page = "home" %}
{% block title %}
{{ title }}
{% endblock %}
{% block script %}
window.onload = function() {
var image = document.getElementById("ui");
function updateImage() {
image.src = image.src.split("?")[0] + "?" + new Date().getTime();
}
setInterval(updateImage, 1000);
}
{% endblock %}
{% block content %}
<img class="ui-image pixelated" src="/ui" id="ui"/>
<div data-role="navbar">
<ul>
<li>
<form class="action" method="post" action="/shutdown"
onsubmit="return confirm('this will halt the unit, continue?');">
<input type="submit" class="button ui-btn ui-corner-all" value="shutdown"/>
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
</form>
<form class="action" method="POST" action="/restart"
onsubmit="return confirm('This will restart the service in {{ other_mode }} mode, continue?');">
<input type="submit" class="button" value="Restart in {{ other_mode }} mode"/>
</li>
<li>
<form class="action" method="post" action="/restart"
onsubmit="return confirm('this will restart the service in {{ other_mode }} mode, continue?');">
<input type="submit" class="button ui-btn ui-corner-all" value="restart in {{ other_mode }} mode"/>
<input type="hidden" name="mode" value="{{ other_mode }}"/>
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
</form>
<form class="action" method="GET" action="/inbox">
<input type="submit" class="button" value="PwnMail"/>
</form>
</div>
</li>
</ul>
</div>
<script type="text/javascript" src="/js/refresh.js"></script>
<script type="text/javascript" src="/js/viewportHeight.js"></script>
</body>
</html>
{% endblock %}

View File

@ -1,53 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
{% extends "base.html" %}
{% set active_page = "" %}
<title>Message from {{ message.sender_name }}</title>
{% block title %}
Message from {{ message.sender_name }}
{% endblock %}
<link rel="stylesheet" href="/js/jquery.mobile/jquery.mobile-1.4.5.min.css"/>
<script type="text/javascript" src="/js/jquery-1.12.4.min.js"></script>
<script type="text/javascript" src="/js/jquery.mobile/jquery.mobile-1.4.5.min.js"></script>
<script type="text/javascript" src="/js/jquery.timeago.js"></script>
<link rel="stylesheet" type="text/css" href="/css/style.css"/>
<script type="text/javascript">
$.mobile.ajaxEnabled = false;
jQuery(document).ready(function() {
jQuery("time.timeago").timeago();
{% block script %}
jQuery(document).ready(function() {
// mark the message as read
jQuery.get("/inbox/{{ message.id }}/seen", function(res){
console.log(res);
});
});
jQuery.get("/inbox/{{ message.id }}/seen", function(res){ console.log(res); });
});
{% endblock %}
</script>
</head>
<body>
<div data-role="page">
{% if error %}
<div class="error">{{ error }}</div>
{% else %}
<div data-role="footer">
<div data-role="navbar">
<ul>
<li><a href="/inbox" id="email" data-icon="back">Back</a></li>
<li><a href="/inbox/new?to={{ message.sender }}" id="reply" data-icon="edit">Reply</a></li>
<li><a href="/inbox/{{ message.id }}/deleted" id="delete" data-icon="delete"
onclick="return confirm('Are you sure?')">Delete</a></li>
</ul>
</div>
</div>
<p class="messagebody">
{% block content %}
<p class="messagebody">
<span style="color: #888">
Message from {{ message.sender_name }}@{{ message.sender }}, received
Message from {{ message.sender_name }}@{{ message.sender }}, sent
<time class="timeago" datetime="{{ message.created_at }}">{{ message.created_at }}</time>
{% if message.seen_at %}, seen
<time class="timeago" datetime="{{ message.seen_at }}">{{ message.seen_at }}</time>{% endif %}.</span>
@ -60,11 +28,10 @@
{% else %}
<small>This message is empty.</small>
{% endif %}
</p>
{% endif %}
</div>
</body>
</html>
<br/>
<br/>
<a href="/inbox/new?to={{ message.sender }}" class="ui-btn ui-icon-comment ui-btn-icon-left ui-shadow-icon">Reply</a>
<a href="/inbox/{{ message.id }}/deleted" class="ui-btn ui-icon-delete ui-btn-icon-left ui-shadow-icon" onclick="return confirm('Are you sure?')">Delete</a>
</p>
{% endblock %}

View File

@ -1,26 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
{% extends "base.html" %}
{% set active_page = "new" %}
{% if to %}
<title>Reply to {{ to }}</title>
{% else %}
<title>New Message</title>
{% endif %}
{% block title %}
{% if to %}
title>Reply to {{ to }}
{% else %}
New Message
{% endif %}
{% endblock %}
<link rel="stylesheet" href="/js/jquery.mobile/jquery.mobile-1.4.5.min.css"/>
<script type="text/javascript" src="/js/jquery-1.12.4.min.js"></script>
<script type="text/javascript" src="/js/jquery.mobile/jquery.mobile-1.4.5.min.js"></script>
<script type="text/javascript" src="/js/jquery.timeago.js"></script>
<link rel="stylesheet" type="text/css" href="/css/style.css"/>
<script type="text/javascript">
$.mobile.ajaxEnabled = false;
$(function(){
{% block script %}
$(function(){
$("#message_form").submit(function(e) {
e.preventDefault();
@ -47,23 +37,11 @@
}
});
});
});
});
{% endblock %}
</script>
</head>
<body>
<div data-role="page">
<div data-role="footer">
<div data-role="navbar">
<ul>
<li><a href="/inbox" id="email" data-icon="back">Back</a></li>
</ul>
</div>
</div>
<div style="padding: 1em">
{% block content %}
<div style="padding: 1em">
<form id="message_form" method="POST" action="/inbox/send">
<label for="to">To:</label>
<input type="text" name="to" id="to" value="{{ to }}">
@ -73,9 +51,5 @@
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<input type="submit" class="button" value="Send"/>
</form>
</div>
</div>
</body>
</html>
{% endblock %}

View File

@ -1,36 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
{% extends "base.html" %}
{% set active_page = "peers" %}
<title>{{ name }} friends</title>
{% block title %}
{{ name }} Friends
{% endblock %}
<link rel="stylesheet" href="/js/jquery.mobile/jquery.mobile-1.4.5.min.css"/>
<script type="text/javascript" src="/js/jquery-1.12.4.min.js"></script>
<script type="text/javascript" src="/js/jquery.mobile/jquery.mobile-1.4.5.min.js"></script>
<script type="text/javascript" src="/js/jquery.timeago.js"></script>
<script type="text/javascript" src="/js/jquery-qrcode-0.17.0.min.js"></script>
<link rel="stylesheet" type="text/css" href="/css/style.css"/>
<script type="text/javascript">
$.mobile.ajaxEnabled = false;
</script>
</head>
<body>
<div data-role="page">
<div data-role="footer">
<div data-role="navbar">
<ul>
<li><a href="/inbox" id="email" data-icon="back">Back</a></li>
</ul>
</div>
</div>
<ul class="peers" data-role="listview" data-filter="true" data-filter-placeholder="Search peers..." data-inset="true">
{% block content %}
<ul class="peers" data-role="listview" data-filter="true" data-filter-placeholder="Search peers..." data-inset="true">
{% for peer in peers %}
<li class="peer">
<a href="/inbox/new?to={{ peer.fingerprint }}">
@ -41,9 +17,5 @@
</a>
</li>
{% endfor %}
</ul>
</div>
</body>
</html>
</ul>
{% endblock %}

View File

@ -1,22 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
{% extends "base.html" %}
{% set active_page = "profile" %}
<title>Profile</title>
{% block title %}
Profile
{% endblock %}
<link rel="stylesheet" href="/js/jquery.mobile/jquery.mobile-1.4.5.min.css"/>
<script type="text/javascript" src="/js/jquery-1.12.4.min.js"></script>
<script type="text/javascript" src="/js/jquery.mobile/jquery.mobile-1.4.5.min.js"></script>
<script type="text/javascript" src="/js/jquery.timeago.js"></script>
<script type="text/javascript" src="/js/jquery-qrcode-0.17.0.min.js"></script>
<link rel="stylesheet" type="text/css" href="/css/style.css"/>
<script type="text/javascript">
$.mobile.ajaxEnabled = false;
$(function(){
{% block script %}
$(function(){
$('#qrcode').qrcode({
render: 'div',
mode: 0,
@ -24,22 +14,11 @@
fontname: 'sans',
fontcolor: '#000'
});
});
</script>
});
{% endblock %}
</head>
<body>
<div data-role="page">
<div data-role="footer">
<div data-role="navbar">
<ul>
<li><a href="/inbox" id="email" data-icon="back">Back</a></li>
</ul>
</div>
</div>
<div style="padding: 1em">
{% block content %}
<div style="padding: 1em">
<label for="name">Name</label>
<h4 id="name">{{ name }}</h4>
@ -52,9 +31,5 @@
<br/>
<label for="data">Data</label>
<pre><code id="data">{{ data }}</code></pre>
</div>
</div>
</body>
</html>
{% endblock %}