app: add a welcome handler

Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
Eric Callahan 2022-03-30 14:39:52 -04:00
parent d11357e5f2
commit 1968beaa0d
No known key found for this signature in database
GPG Key ID: 7027245FBBDDF59A
2 changed files with 264 additions and 1 deletions

View File

@ -11,6 +11,7 @@ import logging
import json
import traceback
import ssl
import pathlib
import urllib.parse
import tornado
import tornado.iostream
@ -205,8 +206,10 @@ class MoonrakerApp:
self.mutable_router = MutableRouter(self)
app_handlers: List[Any] = [
(AnyMatches(), self.mutable_router),
(r"/", WelcomeHandler),
(r"/websocket", WebSocket),
(r"/server/redirect", RedirectHandler)]
(r"/server/redirect", RedirectHandler)
]
self.app = tornado.web.Application(app_handlers, **app_args)
self.get_handler_delegate = self.app.get_handler_delegate
@ -932,3 +935,83 @@ class RedirectHandler(AuthorizedRequestHandler):
raise tornado.web.HTTPError(
400, f"Unauthorized URL redirect: {url}")
self.redirect(url)
class WelcomeHandler(tornado.web.RequestHandler):
def initialize(self) -> None:
self.server: Server = self.settings['server']
def get(self) -> None:
summary: List[str] = []
auth: AuthComp = self.server.lookup_component("authorization", None)
if auth is not None:
try:
user = auth.check_authorized(self.request)
except tornado.web.HTTPError:
authorized = False
else:
authorized = True
if authorized:
summary.append(
"Your device is authorized to access Moonraker's API."
)
else:
summary.append(
"Your device is not authorized to access Moonraker's API. "
"This is normal if you intend to use API Key "
"authentication or log in as an authenticated user. "
"Otherwise you need to add your IP address to the "
"'trusted_clients' option in the [authorization] section "
"of moonraker.conf."
)
cors_enabled = auth.cors_enabled()
if cors_enabled:
summary.append(
"CORS is enabled. Cross origin requests will be allowed "
"for origins that match one of the patterns specified in "
"the 'cors_domain' option of the [authorization] section."
)
else:
summary.append(
"All cross origin requests will be blocked by the browser. "
"The 'cors_domains' option in [authorization] must be "
"configured to enable CORS."
)
else:
authorized = True
cors_enabled = False
summary.append(
"The [authorization] component is not enabled in "
"moonraker.conf. All connections will be considered trusted."
)
summary.append(
"All cross origin requests will be blocked by the browser. "
"The [authorization] section in moonraker.conf must be "
"configured to enable CORS."
)
kstate = self.server.get_klippy_state()
if kstate != "disconnected":
kinfo = self.server.get_klippy_info()
kmsg = kinfo.get("state_message", kstate)
summary.append(f"Klipper reports {kmsg.lower()}")
else:
summary.append(
"Moonraker is not currently connected to Klipper. Make sure "
"that the klipper service has successfully started and that "
"its unix is enabled."
)
wsm: WebsocketManager = self.server.lookup_component("websockets")
context: Dict[str, Any] = {
"ip_address": self.request.remote_ip,
"authorized": authorized,
"cors_enabled": cors_enabled,
"version": self.server.get_app_args()["software_version"],
"ws_count": wsm.get_count(),
"klippy_state": kstate,
"warnings": self.server.get_warnings(),
"summary": summary
}
self.render("welcome.html", **context)
def get_template_path(self) -> Optional[str]:
tpath = pathlib.Path(__file__).parent.joinpath("assets")
return str(tpath)

View File

@ -0,0 +1,180 @@
<html>
<head>
<title>Moonraker {{ version }}</title>
<style>
body {
background-color: rgb(48, 48, 48);
color: rgb(230, 230, 230);
font-family: Arial, Helvetica, sans-serif;
margin: 0px;
}
.nav-bar {
width: 100%;
overflow: hidden;
}
.nav-bar a {
float: right;
text-align: center;
padding: .5em .75em;
font-size: 1.2em;
text-decoration: none;
color: rgb(230, 230, 230);
}
.intro {
font-size: 1.1em;
margin-top: 7rem;
margin-left: auto;
margin-right: auto;
width: 60%;
}
.intro h1 {
font-size: 2.2rem;
text-align: center;
}
.status {
display: flex;
margin-top: 2rem;
margin-left: auto;
margin-right: auto;
width: 80%;
align-items: baseline;
justify-content: center;
column-gap: 1.5rem;
row-gap: 1.5rem;
flex-wrap: wrap;
}
.container {
background:none;
border: 0px;
border-radius: .75rem;
width: 25rem;
}
.container h1 {
background-color: #225353;
text-align: center;
line-height: 2.2rem;
font-size: 1.1rem;
height: 2.2rem;
margin: 0;
border-top-left-radius: .75rem;
border-top-right-radius: .75rem;
}
.container .content{
background-color: #1a1a1a;
border-bottom-left-radius: .75rem;
border-bottom-right-radius: .75rem;
padding: .5rem;
}
.container .entry {
display: inline-block;
width: 100%;
}
.container .entry:not(:last-child) {
margin-bottom: .4rem;
}
.container .value {
float: right;
display: inline;
}
.container ul {
margin: 0px;
padding-left: 1rem;
}
.container li:not(:last-child) {
margin-bottom: .4rem;
}
.messages {
margin-top: 2rem;
width: 51.5rem;
padding: .5rem;
}
.warning h1 {
animation: glow 1s ease-in-out infinite alternate;
}
@keyframes glow {
from {
background-color: #225353;
}
to {
background-color: rgb(160, 64, 8);
}
}
</style>
</head>
<body>
<main>
<div class="nav-bar">
<a href="https://github.com/Arksine/moonraker">GitHub</a>
<a href="https://moonraker.readthedocs.io">Documentation</a>
</div>
<div class="intro">
<h1>Welcome to Moonraker</h1>
<p>You may have intended
to navigate to one of Moonraker's front ends, if so check
that you entered the correct port in the address bar.
</p>
</div>
<div class="status">
<div class="container">
<h1>Authorization</h1>
<div class="content">
<div class="entry">
Request IP:
<div class="value">{{ ip_address }}</div>
</div>
<div class="entry">
Trusted:
<div class="value">{{ authorized}}</div>
</div>
<div class="entry">
CORS Enabled:
<div class="value">{{ cors_enabled }}</div>
</div>
</div>
</div>
<div class="container">
<h1>Status</h1>
<div class="content">
<div class="entry">
Version:
<div class="value">{{ version }}</div>
</div>
<div class="entry">
Websocket Count:
<div class="value">{{ ws_count }}</div>
</div>
<div class="entry">
Klipper State:
<div class="value">{{ klippy_state }}</div>
</div>
</div>
</div>
{% if summary %}
<div class="container messages">
<h1>Summary</h1>
<div class="content">
<ul>
{% for item in summary %}
<li>{{ item }}</li>
{% end %}
</ul>
</div>
</div>
{% end %}
{% if warnings %}
<div class="container messages warning">
<h1>Warnings</h1>
<div class="content">
<ul>
{% for warn in warnings %}
<li>{{ warn }}</li>
{% end %}
</ul>
</div>
</div>
{% end %}
</div>
</main>
</body>
</html>