mirror of
https://github.com/iFargle/headscale-webui.git
synced 2026-03-18 05:36:44 +01:00
Add error screen
This commit is contained in:
82
helper.py
82
helper.py
@@ -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
|
||||
41
server.py
41
server.py
@@ -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
74
templates/error.html
Normal 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>
|
||||
Reference in New Issue
Block a user