Implement response generator for HTML and JSON requests

Supporting updates to web.py and templates
This commit is contained in:
nucleogenic 2022-09-26 00:00:18 +01:00
parent 1e9a7d2283
commit fb8f306005
No known key found for this signature in database
GPG Key ID: 04A5E4E319C4271D
5 changed files with 298 additions and 244 deletions

View File

@ -31,18 +31,33 @@ table, tr, td {
margin: none;
}
.error {
color: white;
font-size:20px;
background-color:red;
white-space: pre-line;
div.flash {
margin-top: 5px;
margin-bottom: 5px;
}
.message {
div.flash div {
color: white;
font-size:20px;
background-color:green;
font-size: 18px;
white-space: pre-line;
padding: 2px 5px;
}
div.flash div.success {
background-color: green;
}
div.flash div.warning {
background-color: orange;
color: black;
}
div.flash div.error {
background-color: red;
}
div.flash div.info {
background-color: #0d6efd;
}
td.inactive {

View File

@ -1,7 +1,7 @@
<!doctype html>
<html>
<head>
<title>{{ _("RaSCSI Reloaded Control Page") }} [{{ host }}]</title>
<title>{{ _("RaSCSI Reloaded Control Page") }} [{{ env["host"] }}]</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<link rel="apple-touch-icon" sizes="57x57" href="/pwa/apple-icon-57x57.png">
@ -26,12 +26,12 @@
<script type="application/javascript">
var processNotify = function(Notification) {
document.getElementById("flash").innerHTML = "<div class='message'>" + Notification + "{{ _(" This process may take a while, and will continue in the background if you navigate away from this page.") }}</div>";
document.getElementById("flash").innerHTML = "<div class=\"info\">" + Notification + "{{ _(" This process may take a while, and will continue in the background if you navigate away from this page.") }}</div>";
window.scrollTo(0,0);
}
var shutdownNotify = function(Notification) {
document.getElementById("flash").innerHTML = "<div class='message'>" + Notification + "{{ _(" The Web Interface will become unresponsive momentarily. Reload this page after the Pi has started up again.") }}</div>";
document.getElementById("flash").innerHTML = "<div class=\"info\">" + Notification + "{{ _(" The Web Interface will become unresponsive momentarily. Reload this page after the Pi has started up again.") }}</div>";
window.scrollTo(0,0);
}
</script>
@ -43,9 +43,9 @@
<body>
<div class="content">
<div class="header">
{% if auth_active %}
{% if username %}
<span style="display: inline-block; width: 100%; color: white; background-color: green; text-align: center; vertical-align: center; font-family: Arial, Helvetica, sans-serif;">{{ _("Logged in as <em>%(username)s</em>", username=username) }} &#8211; <a href="/logout">{{ _("Log Out") }}</a></span>
{% if env["auth_active"] %}
{% if env["username"] %}
<span style="display: inline-block; width: 100%; color: white; background-color: green; text-align: center; vertical-align: center; font-family: Arial, Helvetica, sans-serif;">{{ _("Logged in as <em>%(username)s</em>", username=env["username"]) }} &#8211; <a href="/logout">{{ _("Log Out") }}</a></span>
{% else %}
<span style="display: inline-block; width: 100%; color: white; background-color: red; text-align: center; vertical-align: center; font-family: Arial, Helvetica, sans-serif;">
<form method="POST" action="/login">
@ -70,7 +70,7 @@
</tr>
<tr>
<td style="color: white;">
hostname: {{ host }} ip: {{ ip_addr }}
hostname: {{ env["host"] }} ip: {{ env["ip_addr"] }}
</td>
</tr>
</tbody>
@ -89,8 +89,8 @@
{% block content %}{% endblock content %}
</div>
<div class="footer">
<center><tt>{{ _("RaSCSI Reloaded version: ") }}<strong>{{ version }} <a href="https://github.com/akuker/RASCSI/commit/{{ running_env['git'] }}" target="_blank">{{ running_env["git"][:7] }}</a></strong></tt></center>
<center><tt>{{ _("Pi environment: ") }}{{ running_env["env"] }}</tt></center>
<center><tt>{{ _("RaSCSI Reloaded version: ") }}<strong>{{ version }} <a href="https://github.com/akuker/RASCSI/commit/{{ env["running_env"]["git"] }}" target="_blank">{{ env["running_env"]["git"][:7] }}</a></strong></tt></center>
<center><tt>{{ _("Pi environment: ") }}{{ env["running_env"]["env"] }}</tt></center>
</div>
</div>
</body>

View File

@ -135,7 +135,7 @@
{% endfor %}
</tbody>
</table>
<p><small>{{ _("%(disk_space)s MiB disk space remaining on the Pi", disk_space=free_disk) }}</small></p>
<p><small>{{ _("%(disk_space)s MiB disk space remaining on the Pi", disk_space=env["free_disk_space"]) }}</small></p>
<p><a href="/">{{ _("Cancel") }}</a></p>
{% endblock content %}

View File

@ -309,7 +309,7 @@
{% endfor %}
</tbody>
</table>
<p><small>{{ _("%(disk_space)s MiB disk space remaining on the Pi", disk_space=free_disk) }}</small></p>
<p><small>{{ _("%(disk_space)s MiB disk space remaining on the Pi", disk_space=env["free_disk_space"]) }}</small></p>
<hr/>
<details>

View File

@ -27,6 +27,7 @@ from flask import (
make_response,
session,
abort,
jsonify,
)
from rascsi.ractl_cmds import RaCtlCmds
@ -67,6 +68,66 @@ from settings import (
APP = Flask(__name__)
BABEL = Babel(APP)
def get_env_info():
"""
Get information about the app/host environment
"""
ip_addr, host = sys_cmd.get_ip_and_host()
if "username" in session:
username = session["username"]
else:
username = None
return {
"running_env": sys_cmd.running_env(),
"username": username,
"auth_active": auth_active(AUTH_GROUP)["status"],
"ip_addr": ip_addr,
"host": host,
"free_disk_space": int(sys_cmd.disk_space()["free"] / 1024 / 1024),
}
def response(
template=None,
message=None,
redirect_url=None,
error=False,
status_code=200,
**kwargs
):
"""
Generates a HTML or JSON HTTP response
"""
status = "error" if error else "success"
if isinstance(message, list):
messages = message
elif message is None:
messages = []
else:
messages = [(str(message), status)]
if request.headers.get("accept") == "application/json":
return jsonify({
"status": status,
"messages": [{"message": m, "category": c} for m, c in messages],
"data": kwargs
}), status_code
if messages:
for message, category in messages:
flash(message, category)
if template:
kwargs["env"] = get_env_info()
return render_template(template, **kwargs)
if redirect_url:
return redirect(url_for(redirect_url))
return redirect(url_for("index"))
@BABEL.localeselector
def get_locale():
@ -161,14 +222,12 @@ def index():
else:
username = None
return render_template(
"index.html",
return response(
template="index.html",
locales=get_supported_locales(),
bridge_configured=sys_cmd.is_bridge_setup(),
netatalk_configured=sys_cmd.running_proc("afpd"),
macproxy_configured=sys_cmd.running_proc("macproxy"),
ip_addr=ip_addr,
host=host,
devices=formatted_devices,
files=extended_image_files,
config_files=config_files,
@ -183,20 +242,16 @@ def index():
reserved_scsi_ids=reserved_scsi_ids,
RESERVATIONS=RESERVATIONS,
max_file_size=int(int(MAX_FILE_SIZE) / 1024 / 1024),
running_env=sys_cmd.running_env(),
version=server_info["version"],
log_levels=server_info["log_levels"],
current_log_level=server_info["current_log_level"],
netinfo=ractl_cmd.get_network_info(),
device_types=device_types,
free_disk=int(sys_cmd.disk_space()["free"] / 1024 / 1024),
image_suffixes_to_create=image_suffixes_to_create,
valid_image_suffixes=valid_image_suffixes,
cdrom_file_suffix=tuple(server_info["sccd"]),
removable_file_suffix=tuple(server_info["scrm"]),
mo_file_suffix=tuple(server_info["scmo"]),
username=username,
auth_active=auth_active(AUTH_GROUP)["status"],
PROPERTIES_SUFFIX=PROPERTIES_SUFFIX,
ARCHIVE_FILE_SUFFIXES=ARCHIVE_FILE_SUFFIXES,
REMOVABLE_DEVICE_TYPES=ractl_cmd.get_removable_device_types(),
@ -213,23 +268,20 @@ def drive_list():
# Reads the canonical drive properties into a dict
# The file resides in the current dir of the web ui process
drive_properties = Path(DRIVE_PROPERTIES_FILE)
if drive_properties.is_file():
process = file_cmd.read_drive_properties(str(drive_properties))
process = ReturnCodeMapper.add_msg(process)
if not process["status"]:
flash(process["msg"], "error")
return redirect(url_for("index"))
conf = process["conf"]
else:
flash(
_(
"Could not read drive properties from %(properties_file)s",
properties_file=drive_properties,
),
"error",
)
return redirect(url_for("index"))
if not drive_properties.is_file():
return response(
error=True,
message=_("Could not read drive properties from %(properties_file)s",
properties_file=drive_properties),
)
process = file_cmd.read_drive_properties(str(drive_properties))
process = ReturnCodeMapper.add_msg(process)
if not process["status"]:
return response(error=True, message=process["msg"])
conf = process["conf"]
hd_conf = []
cd_conf = []
rm_conf = []
@ -247,30 +299,18 @@ def drive_list():
device["size_mb"] = "{:,.2f}".format(device["size"] / 1024 / 1024)
rm_conf.append(device)
if "username" in session:
username = session["username"]
else:
username = None
server_info = ractl_cmd.get_server_info()
ip_addr, host = sys_cmd.get_ip_and_host()
return render_template(
return response(
"drives.html",
files=file_cmd.list_images()["files"],
base_dir=server_info["image_dir"],
hd_conf=hd_conf,
cd_conf=cd_conf,
rm_conf=rm_conf,
running_env=sys_cmd.running_env(),
version=server_info["version"],
free_disk=int(sys_cmd.disk_space()["free"] / 1024 / 1024),
cdrom_file_suffix=tuple(server_info["sccd"]),
username=username,
auth_active=auth_active(AUTH_GROUP)["status"],
ip_addr=ip_addr,
host=host,
)
)
@APP.route("/login", methods=["POST"])
@ -280,20 +320,17 @@ def login():
"""
username = request.form["username"]
password = request.form["password"]
groups = [g.gr_name for g in getgrall() if username in g.gr_mem]
if AUTH_GROUP in groups:
if authenticate(str(username), str(password)):
session["username"] = request.form["username"]
return redirect(url_for("index"))
flash(
_(
"You must log in with credentials for a user in the '%(group)s' group",
group=AUTH_GROUP,
),
"error",
)
return redirect(url_for("index"))
return response()
return response(error=True, status_code=401, message=_(
"You must log in with valid credentials for a user in the '%(group)s' group",
group=AUTH_GROUP,
))
@APP.route("/logout")
@ -302,7 +339,7 @@ def logout():
Removes the logged in user from the session
"""
session.pop("username", None)
return redirect(url_for("index"))
return response()
@APP.route("/pwa/<path:pwa_path>")
@ -321,8 +358,7 @@ def login_required(func):
def decorated_function(*args, **kwargs):
auth = auth_active(AUTH_GROUP)
if auth["status"] and "username" not in session:
flash(auth["msg"], "error")
return redirect(url_for("index"))
return response(error=True, message=auth["msg"])
return func(*args, **kwargs)
return decorated_function
@ -344,11 +380,8 @@ def drive_create():
# Creating the image file
process = file_cmd.create_new_image(file_name, file_type, size)
if process["status"]:
flash(_("Image file created: %(file_name)s", file_name=full_file_name))
else:
flash(process["msg"], "error")
return redirect(url_for("index"))
if not process["status"]:
return response(error=True, message=process["msg"])
# Creating the drive properties file
prop_file_name = f"{file_name}.{file_type}.{PROPERTIES_SUFFIX}"
@ -360,12 +393,10 @@ def drive_create():
}
process = file_cmd.write_drive_properties(prop_file_name, properties)
process = ReturnCodeMapper.add_msg(process)
if process["status"]:
flash(process["msg"])
return redirect(url_for("index"))
if not process["status"]:
return response(error=True, message=process["msg"])
flash(process['msg'], "error")
return redirect(url_for("index"))
return response(message=_("Image file created: %(file_name)s", file_name=full_file_name))
@APP.route("/drive/cdrom", methods=["POST"])
@ -391,11 +422,9 @@ def drive_cdrom():
process = file_cmd.write_drive_properties(file_name, properties)
process = ReturnCodeMapper.add_msg(process)
if process["status"]:
flash(process["msg"])
return redirect(url_for("index"))
return response(message=process["msg"])
flash(process['msg'], "error")
return redirect(url_for("index"))
return response(error=True, message=process["msg"])
@APP.route("/config/save", methods=["POST"])
@ -410,11 +439,9 @@ def config_save():
process = file_cmd.write_config(file_name)
process = ReturnCodeMapper.add_msg(process)
if process["status"]:
flash(process["msg"])
return redirect(url_for("index"))
return response(message=process["msg"])
flash(process['msg'], "error")
return redirect(url_for("index"))
return response(error=True, message=process["msg"])
@APP.route("/config/load", methods=["POST"])
@ -429,24 +456,19 @@ def config_load():
process = file_cmd.read_config(file_name)
process = ReturnCodeMapper.add_msg(process)
if process["status"]:
flash(process["msg"])
return redirect(url_for("index"))
return response(message=process["msg"])
return response(error=True, message=process["msg"])
flash(process['msg'], "error")
return redirect(url_for("index"))
if "delete" in request.form:
process = file_cmd.delete_file(f"{CFG_DIR}/{file_name}")
process = ReturnCodeMapper.add_msg(process)
if process["status"]:
flash(process["msg"])
return redirect(url_for("index"))
return response(message=process["msg"])
flash(process['msg'], "error")
return redirect(url_for("index"))
return response(error=True, message=process["msg"])
# The only reason we would reach here would be a Web UI bug. Will not localize.
flash("Got an unhandled request (needs to be either load or delete)", "error")
return redirect(url_for("index"))
return response(error=True, message="Action field (load, delete) missing")
@APP.route("/logs/show", methods=["POST"])
@ -457,14 +479,16 @@ def show_logs():
lines = request.form.get("lines")
scope = request.form.get("scope")
# TODO: Render logs in template
returncode, logs = sys_cmd.get_logs(lines, scope)
if returncode == 0:
headers = {"content-type": "text/plain"}
return logs, int(lines), headers
return logs, headers
flash(_("An error occurred when fetching logs."))
flash(logs, "stderr")
return redirect(url_for("index"))
return response(error=True, message=[
(_("An error occurred when fetching logs."), "error"),
(logs, "stderr"),
])
@APP.route("/logs/level", methods=["POST"])
@ -477,11 +501,9 @@ def log_level():
process = ractl_cmd.set_log_level(level)
if process["status"]:
flash(_("Log level set to %(value)s", value=level))
return redirect(url_for("index"))
return response(message=_("Log level set to %(value)s", value=level))
flash(process["msg"], "error")
return redirect(url_for("index"))
return response(error=True, message=process["msg"])
@APP.route("/scsi/attach_device", methods=["POST"])
@ -507,11 +529,14 @@ def attach_device():
error_msg = _("Please follow the instructions at %(url)s", url=error_url)
if "interface" in params.keys():
# TODO: Can the behaviour of this function be made more intuitive?
bridge_status = is_bridge_configured(params["interface"])
# Error condition is truthy
if bridge_status:
flash(bridge_status, "error")
flash(error_msg, "error")
return redirect(url_for("index"))
return response(error=True, message=[
(bridge_status, "error"),
(error_msg, "error")
])
kwargs = {
"unit": int(unit),
@ -521,16 +546,14 @@ def attach_device():
process = ractl_cmd.attach_device(scsi_id, **kwargs)
process = ReturnCodeMapper.add_msg(process)
if process["status"]:
flash(_(
return response(message=_(
"Attached %(device_type)s to SCSI ID %(id_number)s LUN %(unit_number)s",
device_type=get_device_name(device_type),
id_number=scsi_id,
unit_number=unit,
))
return redirect(url_for("index"))
flash(process["msg"], "error")
return redirect(url_for("index"))
return response(error=True, message=process["msg"])
@APP.route("/scsi/attach", methods=["POST"])
@ -559,8 +582,7 @@ def attach_image():
process = file_cmd.read_drive_properties(drive_properties)
process = ReturnCodeMapper.add_msg(process)
if not process["status"]:
flash(process["msg"], "error")
return redirect(url_for("index"))
return response(error=True, message=process["msg"])
conf = process["conf"]
kwargs["vendor"] = conf["vendor"]
kwargs["product"] = conf["product"]
@ -571,28 +593,31 @@ def attach_image():
process = ractl_cmd.attach_device(scsi_id, **kwargs)
process = ReturnCodeMapper.add_msg(process)
if process["status"]:
flash(_(
response_messages = [(_(
"Attached %(file_name)s as %(device_type)s to "
"SCSI ID %(id_number)s LUN %(unit_number)s",
file_name=file_name,
device_type=get_device_name(device_type),
id_number=scsi_id,
unit_number=unit,
))
), "success")]
if int(file_size) % int(expected_block_size):
flash(_(
response_messages.append((_(
"The image file size %(file_size)s bytes is not a multiple of "
"%(block_size)s. RaSCSI will ignore the trailing data. "
"The image may be corrupted, so proceed with caution.",
file_size=file_size,
block_size=expected_block_size,
), "error")
return redirect(url_for("index"))
), "warning"))
flash(_("Failed to attach %(file_name)s to SCSI ID %(id_number)s LUN %(unit_number)s",
file_name=file_name, id_number=scsi_id, unit_number=unit), "error")
flash(process["msg"], "error")
return redirect(url_for("index"))
return response(message=response_messages)
return response(error=True, message=[
(_("Failed to attach %(file_name)s to SCSI ID %(id_number)s LUN %(unit_number)s",
file_name=file_name, id_number=scsi_id, unit_number=unit), "error"),
(process["msg"], "error"),
])
@APP.route("/scsi/detach_all", methods=["POST"])
@ -603,11 +628,9 @@ def detach_all_devices():
"""
process = ractl_cmd.detach_all()
if process["status"]:
flash(_("Detached all SCSI devices"))
return redirect(url_for("index"))
return response(message=_("Detached all SCSI devices"))
flash(process["msg"], "error")
return redirect(url_for("index"))
return response(error=True, message=process["msg"])
@APP.route("/scsi/detach", methods=["POST"])
@ -620,14 +643,14 @@ def detach():
unit = request.form.get("unit")
process = ractl_cmd.detach_by_id(scsi_id, unit)
if process["status"]:
flash(_("Detached SCSI ID %(id_number)s LUN %(unit_number)s",
id_number=scsi_id, unit_number=unit))
return redirect(url_for("index"))
return response(message=_("Detached SCSI ID %(id_number)s LUN %(unit_number)s",
id_number=scsi_id, unit_number=unit))
flash(_("Failed to detach SCSI ID %(id_number)s LUN %(unit_number)s",
id_number=scsi_id, unit_number=unit), "error")
flash(process["msg"], "error")
return redirect(url_for("index"))
return response(error=True, message=[
(_("Failed to detach SCSI ID %(id_number)s LUN %(unit_number)s",
id_number=scsi_id, unit_number=unit), "error"),
(process["msg"], "error"),
])
@APP.route("/scsi/eject", methods=["POST"])
@ -641,14 +664,15 @@ def eject():
process = ractl_cmd.eject_by_id(scsi_id, unit)
if process["status"]:
flash(_("Ejected SCSI ID %(id_number)s LUN %(unit_number)s",
id_number=scsi_id, unit_number=unit))
return redirect(url_for("index"))
return response(message=_("Ejected SCSI ID %(id_number)s LUN %(unit_number)s",
id_number=scsi_id, unit_number=unit))
return response(error=True, message=[
(_("Failed to eject SCSI ID %(id_number)s LUN %(unit_number)s",
id_number=scsi_id, unit_number=unit), "error"),
(process["msg"], "error"),
])
flash(_("Failed to eject SCSI ID %(id_number)s LUN %(unit_number)s",
id_number=scsi_id, unit_number=unit), "error")
flash(process["msg"], "error")
return redirect(url_for("index"))
@APP.route("/scsi/info", methods=["POST"])
def device_info():
@ -662,29 +686,47 @@ def device_info():
# First check if any device at all was returned
if not devices["status"]:
flash(devices["msg"], "error")
return redirect(url_for("index"))
return response(error=True, message=devices["msg"])
# Looking at the first dict in list to get
# the one and only device that should have been returned
device = devices["device_list"][0]
if str(device["id"]) == scsi_id:
flash(_("DEVICE INFO"))
flash("===========")
flash(_("SCSI ID: %(id_number)s", id_number=device["id"]))
flash(_("LUN: %(unit_number)s", unit_number=device["unit"]))
flash(_("Type: %(device_type)s", device_type=device["device_type"]))
flash(_("Status: %(device_status)s", device_status=device["status"]))
flash(_("File: %(image_file)s", image_file=device["image"]))
flash(_("Parameters: %(value)s", value=device["params"]))
flash(_("Vendor: %(value)s", value=device["vendor"]))
flash(_("Product: %(value)s", value=device["product"]))
flash(_("Revision: %(revision_number)s", revision_number=device["revision"]))
flash(_("Block Size: %(value)s bytes", value=device["block_size"]))
flash(_("Image Size: %(value)s bytes", value=device["size"]))
return redirect(url_for("index"))
message = "\n".join([
_("DEVICE INFO"),
"===========",
_("SCSI ID: %(id_number)s", id_number=device["id"]),
_("LUN: %(unit_number)s", unit_number=device["unit"]),
_("Type: %(device_type)s", device_type=device["device_type"]),
_("Status: %(device_status)s", device_status=device["status"]),
_("File: %(image_file)s", image_file=device["image"]),
_("Parameters: %(value)s", value=device["params"]),
_("Vendor: %(value)s", value=device["vendor"]),
_("Product: %(value)s", value=device["product"]),
_("Revision: %(revision_number)s", revision_number=device["revision"]),
_("Block Size: %(value)s bytes", value=device["block_size"]),
_("Image Size: %(value)s bytes", value=device["size"]),
])
# Don't send redundant "info" message with the JSON response
if request.headers.get("accept") == "application/json":
return response(device_info={
"scsi_id": device["id"],
"lun": device["unit"],
"device_type": device["device_type"],
"status": device["status"],
"file": device["image"],
"parameters": device["params"],
"vendor": device["vendor"],
"product": device["product"],
"revision": device["revision"],
"block_size": device["block_size"],
"size": device["size"],
})
return response(message=[(message, "info")])
return response(error=True, message=devices["msg"])
flash(devices["msg"], "error")
return redirect(url_for("index"))
@APP.route("/scsi/reserve", methods=["POST"])
@login_required
@ -699,12 +741,13 @@ def reserve_id():
process = ractl_cmd.reserve_scsi_ids(reserved_ids)
if process["status"]:
RESERVATIONS[int(scsi_id)] = memo
flash(_("Reserved SCSI ID %(id_number)s", id_number=scsi_id))
return redirect(url_for("index"))
return response(message=_("Reserved SCSI ID %(id_number)s", id_number=scsi_id))
return response(error=True, message=[
(_("Failed to reserve SCSI ID %(id_number)s", id_number=scsi_id), "error"),
(process["msg"], "error"),
])
flash(_("Failed to reserve SCSI ID %(id_number)s", id_number=scsi_id))
flash(process["msg"], "error")
return redirect(url_for("index"))
@APP.route("/scsi/release", methods=["POST"])
@login_required
@ -718,12 +761,12 @@ def release_id():
process = ractl_cmd.reserve_scsi_ids(reserved_ids)
if process["status"]:
RESERVATIONS[int(scsi_id)] = ""
flash(_("Released the reservation for SCSI ID %(id_number)s", id_number=scsi_id))
return redirect(url_for("index"))
return response(message=_("Released the reservation for SCSI ID %(id_number)s", id_number=scsi_id))
flash(_("Failed to release the reservation for SCSI ID %(id_number)s", id_number=scsi_id))
flash(process["msg"], "error")
return redirect(url_for("index"))
return response(error=True, message=[
(_("Failed to release the reservation for SCSI ID %(id_number)s", id_number=scsi_id), "error"),
(process["msg"], "error"),
])
@APP.route("/pi/reboot", methods=["POST"])
@ -733,7 +776,7 @@ def restart():
Restarts the Pi
"""
ractl_cmd.shutdown_pi("reboot")
return redirect(url_for("index"))
return response()
@APP.route("/pi/shutdown", methods=["POST"])
@ -743,7 +786,7 @@ def shutdown():
Shuts down the Pi
"""
ractl_cmd.shutdown_pi("system")
return redirect(url_for("index"))
return response()
@APP.route("/files/download_to_iso", methods=["POST"])
@ -755,16 +798,18 @@ def download_to_iso():
scsi_id = request.form.get("scsi_id")
url = request.form.get("url")
iso_args = request.form.get("type").split()
response_messages = []
process = file_cmd.download_file_to_iso(url, *iso_args)
process = ReturnCodeMapper.add_msg(process)
if process["status"]:
flash(process["msg"])
flash(_("Saved image as: %(file_name)s", file_name=process['file_name']))
else:
flash(_("Failed to create CD-ROM image from %(url)s", url=url), "error")
flash(process["msg"], "error")
return redirect(url_for("index"))
if not process["status"]:
return response(error=True, message=[
(_("Failed to create CD-ROM image from %(url)s", url=url), "error"),
(process["msg"], "error"),
])
response_messages.append((process["msg"], "success"))
response_messages.append((_("Saved image as: %(file_name)s", file_name=process['file_name']), "success"))
process_attach = ractl_cmd.attach_device(
scsi_id,
@ -773,13 +818,13 @@ def download_to_iso():
)
process_attach = ReturnCodeMapper.add_msg(process_attach)
if process_attach["status"]:
flash(_("Attached to SCSI ID %(id_number)s", id_number=scsi_id))
return redirect(url_for("index"))
response_messages.append((_("Attached to SCSI ID %(id_number)s", id_number=scsi_id), "success"))
return response(message=response_messages)
flash(_("Failed to attach image to SCSI ID %(id_number)s. Try attaching it manually.",
id_number=scsi_id), "error")
flash(process_attach["msg"], "error")
return redirect(url_for("index"))
return response(error=True, message=[
(_("Failed to attach image to SCSI ID %(id_number)s. Try attaching it manually.", id_number=scsi_id), "error"),
(process_attach["msg"], "error"),
])
@APP.route("/files/download_to_images", methods=["POST"])
@ -793,12 +838,12 @@ def download_img():
process = file_cmd.download_to_dir(url, server_info["image_dir"], Path(url).name)
process = ReturnCodeMapper.add_msg(process)
if process["status"]:
flash(process["msg"])
return redirect(url_for("index"))
return response(message=process["msg"])
flash(_("Failed to download file from %(url)s", url=url), "error")
flash(process["msg"], "error")
return redirect(url_for("index"))
return response(error=True, message=[
(_("Failed to download file from %(url)s", url=url), "error"),
(process["msg"], "error"),
])
@APP.route("/files/download_to_afp", methods=["POST"])
@ -812,12 +857,12 @@ def download_afp():
process = file_cmd.download_to_dir(url, AFP_DIR, file_name)
process = ReturnCodeMapper.add_msg(process)
if process["status"]:
flash(process["msg"])
return redirect(url_for("index"))
return response(message=process["msg"])
flash(_("Failed to download file from %(url)s", url=url), "error")
flash(process["msg"], "error")
return redirect(url_for("index"))
return response(error=True, message=[
(_("Failed to download file from %(url)s", url=url), "error"),
(process["msg"], "error"),
])
@APP.route("/files/upload", methods=["POST"])
@ -848,11 +893,13 @@ def create_file():
process = file_cmd.create_new_image(file_name, file_type, size)
if process["status"]:
flash(_("Image file created: %(file_name)s", file_name=full_file_name))
return redirect(url_for("index"))
return response(
status_code=201,
message=_("Image file created: %(file_name)s", file_name=full_file_name),
image=full_file_name,
)
flash(process["msg"], "error")
return redirect(url_for("index"))
return response(error=True, message=process["msg"])
@APP.route("/files/download", methods=["POST"])
@ -874,26 +921,23 @@ def delete():
file_name = request.form.get("file_name")
process = file_cmd.delete_image(file_name)
if process["status"]:
flash(_("Image file deleted: %(file_name)s", file_name=file_name))
else:
flash(process["msg"], "error")
return redirect(url_for("index"))
if not process["status"]:
return response(error=True, message=process["msg"])
response_messages = [
(_("Image file deleted: %(file_name)s", file_name=file_name), "success")]
# Delete the drive properties file, if it exists
prop_file_path = f"{CFG_DIR}/{file_name}.{PROPERTIES_SUFFIX}"
if Path(prop_file_path).is_file():
process = file_cmd.delete_file(prop_file_path)
process = ReturnCodeMapper.add_msg(process)
if process["status"]:
flash(process["msg"])
return redirect(url_for("index"))
response_messages.append((process["msg"], "success"))
else:
response_messages.append((process["msg"], "error"))
flash(process["msg"], "error")
return redirect(url_for("index"))
return redirect(url_for("index"))
return response(message=response_messages)
@APP.route("/files/rename", methods=["POST"])
@ -906,11 +950,11 @@ def rename():
new_file_name = request.form.get("new_file_name")
process = file_cmd.rename_image(file_name, new_file_name)
if process["status"]:
flash(_("Image file renamed to: %(file_name)s", file_name=new_file_name))
else:
flash(process["msg"], "error")
return redirect(url_for("index"))
if not process["status"]:
return response(error=True, message=process["msg"])
response_messages = [
(_("Image file renamed to: %(file_name)s", file_name=new_file_name), "success")]
# Rename the drive properties file, if it exists
prop_file_path = f"{CFG_DIR}/{file_name}.{PROPERTIES_SUFFIX}"
@ -919,13 +963,11 @@ def rename():
process = file_cmd.rename_file(prop_file_path, new_prop_file_path)
process = ReturnCodeMapper.add_msg(process)
if process["status"]:
flash(process["msg"])
return redirect(url_for("index"))
response_messages.append((process["msg"], "success"))
else:
response_messages.append((process["msg"], "error"))
flash(process["msg"], "error")
return redirect(url_for("index"))
return redirect(url_for("index"))
return response(message=response_messages)
@APP.route("/files/copy", methods=["POST"])
@ -938,11 +980,11 @@ def copy():
new_file_name = request.form.get("copy_file_name")
process = file_cmd.copy_image(file_name, new_file_name)
if process["status"]:
flash(_("Copy of image file saved as: %(file_name)s", file_name=new_file_name))
else:
flash(process["msg"], "error")
return redirect(url_for("index"))
if not process["status"]:
return response(error=True, message=process["msg"])
response_messages = [
(_("Copy of image file saved as: %(file_name)s", file_name=new_file_name), "success")]
# Create a copy of the drive properties file, if it exists
prop_file_path = f"{CFG_DIR}/{file_name}.{PROPERTIES_SUFFIX}"
@ -951,13 +993,11 @@ def copy():
process = file_cmd.copy_file(prop_file_path, new_prop_file_path)
process = ReturnCodeMapper.add_msg(process)
if process["status"]:
flash(process["msg"])
return redirect(url_for("index"))
response_messages.append((process["msg"], "success"))
else:
response_messages.append((process["msg"], "error"))
flash(process["msg"], "error")
return redirect(url_for("index"))
return redirect(url_for("index"))
return response(message=response_messages)
@APP.route("/files/extract_image", methods=["POST"])
@ -976,23 +1016,23 @@ def extract_image():
)
if extract_result["return_code"] == ReturnCodes.EXTRACTIMAGE_SUCCESS:
flash(ReturnCodeMapper.add_msg(extract_result).get("msg"))
response_messages = [(ReturnCodeMapper.add_msg(extract_result).get("msg"), "success")]
for properties_file in extract_result["properties_files_moved"]:
if properties_file["status"]:
flash(_("Properties file %(file)s moved to %(directory)s",
response_messages.append((_("Properties file %(file)s moved to %(directory)s",
file=properties_file['name'],
directory=CFG_DIR
))
), "success"))
else:
flash(_("Failed to move properties file %(file)s to %(directory)s",
response_messages.append((_("Failed to move properties file %(file)s to %(directory)s",
file=properties_file['name'],
directory=CFG_DIR
), "error")
else:
flash(ReturnCodeMapper.add_msg(extract_result).get("msg"), "error")
), "error"))
return redirect(url_for("index"))
return response(message=response_messages)
return response(error=True, message=ReturnCodeMapper.add_msg(extract_result).get("msg"))
@APP.route("/language", methods=["POST"])
@ -1008,8 +1048,7 @@ def change_language():
language = Locale.parse(locale)
language_name = language.get_language_name(locale)
flash(_("Changed Web Interface language to %(locale)s", locale=language_name))
return redirect(url_for("index"))
return response(message=_("Changed Web Interface language to %(locale)s", locale=language_name))
@APP.before_first_request