Add error screen

This commit is contained in:
iFargle
2023-02-15 12:31:22 +09:00
parent 69fd3e9bec
commit e0f13f85df
3 changed files with 182 additions and 15 deletions

View File

@@ -1,5 +1,5 @@
import logging, sys, pytz, os, headscale
import logging, sys, pytz, os, headscale, requests
from datetime import datetime, timedelta, date
from dateutil import parser
@@ -84,4 +84,82 @@ def get_color(id, type = ""):
"grey lighten-1",
]
index = id % len(colors)
return colors[index]
return colors[index]
def error_message_format(type, title, message):
content = """
<ul class="collection">
<li class="collection-item avatar">
"""
match type.lower():
case "warning":
icon = """<i class="material-icons circle yellow">priority_high</i>"""
title = "<span class="title">Warning</span>"
case "success":
icon = """<i class="material-icons circle green">check</i>"""
title = "<span class="title">Success</span>"
case "error":
icon = """<i class="material-icons circle red">warning</i>"""
title = "<span class="title">Error</span>"
case "information":
icon = """<i class="material-icons circle grey">help</i>"""
title = "<span class="title">Information</span>"
content = content+icon+title+message
content = content+"""
</li>
</ul>
"""
return content
def startup_checks():
url = headscale.get_url()
# Return an error message if things fail.
# Return a formatted error message for EACH fail.
# Otherwise, return "Pass"
pass = True
# Check 1: See if the Headscale server is reachable:
reachable = NULL
response = requests.delete(
str(url)+"/api/v1/,
headers={
'Accept': 'application/json',
'Authorization': 'Bearer '+str(api_key)
}
)
if response.status_code == 200:
reachable = True
else:
reachable = False
pass = False
# Check 2: See if /data/ is writable:
writable = NULL
try:
key_file = open("/data/key.txt", "wb+")
writable = True
except PermissionError:
writable = False
pass = False
if pass: return "Pass"
messageHTML = ""
# Generate the message:
if not reachable:
message = """
<p>Your headscale server is either unreachable or not properly configured.
Please ensure your configuration is correct (Check for 200 status on
"""+url+"""/api/v1 failed.)</p>
"""
messageHTML += format_error_message("Error", "Headscale unreachable", message)
if not writable:
message = """
<p>/data/key.txt is not writable. Please ensure your
permissions are correct. /data mount should be writable
by UID/GID 1000:1000</p>
"""
messageHTML += format_error_message("Error", "/data not writable", message)
return messageHTML

View File

@@ -1,5 +1,5 @@
import requests, json, renderer, headscale, helper, logging, sys, pytz, os, time
from flask import Flask, render_template, request, url_for, redirect
from flask import Flask, render_template, request, url_for, redirect, Markup
from datetime import datetime, timedelta, date
from dateutil import parser
@@ -47,6 +47,8 @@ app.logger.debug("BASE_PATH: "+BASE_PATH)
def overview_page():
# If the API key fails, redirect to the settings page:
if not helper.key_test(): return redirect(BASE_PATH+url_for('settings_page'))
# General error checks. See the function for more info:
if helper.startup_checks() != "Pass": return redirect(BASE_PATH+url_for('error_page'))
return render_template('overview.html',
render_page = renderer.render_overview(),
@@ -59,7 +61,9 @@ def overview_page():
def machines_page():
# If the API key fails, redirect to the settings page:
if not helper.key_test(): return redirect(BASE_PATH+url_for('settings_page'))
# General error checks. See the function for more info:
if helper.startup_checks() != "Pass": return redirect(BASE_PATH+url_for('error_page'))
cards = renderer.render_machines_cards()
return render_template('machines.html',
cards = cards,
@@ -73,6 +77,8 @@ def machines_page():
def users_page():
# If the API key fails, redirect to the settings page:
if not helper.key_test(): return redirect(BASE_PATH+url_for('settings_page'))
# General error checks. See the function for more info:
if helper.startup_checks() != "Pass": return redirect(BASE_PATH+url_for('error_page'))
cards = renderer.render_users_cards()
return render_template('users.html',
@@ -85,19 +91,29 @@ def users_page():
@app.route(BASE_PATH+'/settings', methods=('GET', 'POST'))
@app.route('/settings', methods=('GET', 'POST'))
def settings_page():
# General error checks. See the function for more info:
if helper.startup_checks() != "Pass": return redirect(BASE_PATH+url_for('error_page'))
url = headscale.get_url()
api_key = headscale.get_api_key()
return render_template('settings.html',
url = url,
COLOR_NAV = COLOR_NAV,
COLOR_BTN = COLOR_BTN,
HS_VERSION = HS_VERSION,
APP_VERSION = APP_VERSION,
GIT_COMMIT = GIT_COMMIT,
GIT_BRANCH = GIT_BRANCH,
BUILD_DATE = BUILD_DATE
)
return render_template(
'settings.html',
url = url,
COLOR_NAV = COLOR_NAV,
COLOR_BTN = COLOR_BTN,
HS_VERSION = HS_VERSION,
APP_VERSION = APP_VERSION,
GIT_COMMIT = GIT_COMMIT,
GIT_BRANCH = GIT_BRANCH,
BUILD_DATE = BUILD_DATE
)
@app.route(BASE_PATH+'/error', methods=('GET'))
@app.route('/error', methods=('GET'))
def error_page():
return render_template('error.html',
ERROR_MESSAGE = helper.startup_checks()
)
########################################################################################
# /api pages
@@ -170,7 +186,6 @@ def save_key_page():
else: return "Key failed testing. Check your key"
else: return "Key did not save properly. Check logs"
########################################################################################
# Machine API Endpoints
########################################################################################

74
templates/error.html Normal file
View File

@@ -0,0 +1,74 @@
<!DOCTYPE html>
<html id="page">
<head>
<title>●::[●●] {% block title %} {% endblock %}</title>
<link rel="apple-touch-icon" sizes="57x57" href="static/img/favicon/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="static/img/favicon/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="static/img/favicon/apple-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="static/img/favicon/apple-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="static/img/favicon/apple-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="static/img/favicon/apple-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="static/img/favicon/apple-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="static/img/favicon/apple-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="static/img/favicon/apple-icon-180x180.png">
<link rel="icon" type="image/png" sizes="192x192" href="static/img/favicon/android-icon-192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="static/img/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="static/img/favicon/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="16x16" href="static/img/favicon/favicon-16x16.png">
<link rel="manifest" href="static/img/favicon/manifest.json">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="/ms-icon-144x144.png">
<meta name="theme-color" content="#ffffff">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<!-- CSS and Icons -->
<link href="static/css/materialize.min.css" type="text/css" rel="stylesheet"/>
<link href="static/css/overrides.css" type="text/css" rel="stylesheet"/>
<style>
/* fallback */
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
/* src: url(https://fonts.gstatic.com/s/materialicons/v139/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2) format('woff2'); */
src: url(static/fonts/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2) format('woff2');
}
.material-icons {
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 24px;
line-height: 1;
letter-spacing: normal;
text-transform: none;
display: inline-block;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
-moz-font-feature-settings: 'liga';
-moz-osx-font-smoothing: grayscale;
}
</style>
</head>
<body>
<div class="container">
{{ ERROR_MESSAGE }}
</div>
<!-- Modals -->
<div id="generic_modal" class="modal">
<div class="modal-content">
<h4 id="generic_modal_title"></h4>
<p id="generic_modal_content"></p>
</div>
<div class="modal-footer">
<a href="#!" class="modal-close btn-flat">Cancel</a>
</div>
</div>
</body>
<script type="text/javascript" src="static/js/jquery-2.2.4.min.js"></script>
<script type="text/javascript" src="static/js/materialize.min.js"></script>
<script>M.AutoInit();</script>
<script type="text/javascript" src="static/js/custom.js"></script>
</html>