mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
Made advancements in instances page - web UI
This commit is contained in:
parent
8b40406612
commit
1b76c2f0a9
14 changed files with 277 additions and 88 deletions
|
|
@ -18,17 +18,17 @@ def instances_page():
|
|||
return render_template("instances.html", instances=BW_INSTANCES_UTILS.get_instances())
|
||||
|
||||
|
||||
@instances.route("/instances/new", methods=["PUT"])
|
||||
@instances.route("/instances/new", methods=["POST"])
|
||||
@login_required
|
||||
def instances_new():
|
||||
verify_data_in_form(
|
||||
data={"instance_hostname": None},
|
||||
data={"hostname": None},
|
||||
err_message="Missing instance hostname parameter on /instances/new.",
|
||||
redirect_url="instances",
|
||||
next=True,
|
||||
)
|
||||
verify_data_in_form(
|
||||
data={"instance_name": None},
|
||||
data={"name": None},
|
||||
err_message="Missing instance name parameter on /instances/new.",
|
||||
redirect_url="instances",
|
||||
next=True,
|
||||
|
|
@ -37,10 +37,10 @@ def instances_new():
|
|||
db_config = BW_CONFIG.get_config(global_only=True, methods=False, filtered_settings=("API_HTTP_PORT", "API_SERVER_NAME"))
|
||||
|
||||
instance = {
|
||||
"hostname": request.form["instance_hostname"].replace("http://", "").replace("https://", ""),
|
||||
"name": request.form["instance_name"],
|
||||
"port": db_config["API_HTTP_PORT"],
|
||||
"server_name": db_config["API_SERVER_NAME"],
|
||||
"hostname": request.form["hostname"].replace("http://", "").replace("https://", "").split(":")[0],
|
||||
"name": request.form["name"],
|
||||
"port": db_config.get("API_HTTP_PORT", "5000"),
|
||||
"server_name": db_config.get("API_SERVER_NAME", "bwapi"),
|
||||
"method": "ui",
|
||||
}
|
||||
|
||||
|
|
@ -87,9 +87,8 @@ def instances_action(instance_hostname: str, action: Literal["ping", "reload", "
|
|||
"loading",
|
||||
next=url_for("instances.instances_page"),
|
||||
message=(
|
||||
f"{action.title()}ing"
|
||||
if action not in ("delete", "stop")
|
||||
else ("Deleting" if action == "delete" else "Stopping") + f" instance {instance_hostname}"
|
||||
(f"{action.title()}ing" if action not in ("delete", "stop") else ("Deleting" if action == "delete" else "Stopping"))
|
||||
+ f" instance {instance_hostname}"
|
||||
),
|
||||
)
|
||||
)
|
||||
|
|
|
|||
3
src/ui/app/static/css/pages/instances.css
Normal file
3
src/ui/app/static/css/pages/instances.css
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
td.highlight {
|
||||
background-color: rgba(var(--bs-primary-rgb), 0.1) !important;
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.layout-main-info h2 {
|
||||
.layout-main-info h3 {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +1,108 @@
|
|||
$(document).ready(function () {
|
||||
new DataTable("#instances", {
|
||||
$.fn.dataTable.ext.buttons.create_instance = {
|
||||
text: "Create new instance",
|
||||
className: "btn btn-sm btn-outline-primary",
|
||||
action: function (e, dt, node, config) {
|
||||
var modal = new bootstrap.Modal($("#modal-create-instance"));
|
||||
modal.show();
|
||||
},
|
||||
};
|
||||
|
||||
const instances_table = new DataTable("#instances", {
|
||||
columnDefs: [{ orderable: false, targets: 7 }],
|
||||
order: [[6, "desc"]],
|
||||
autoFill: false,
|
||||
colReorder: true,
|
||||
responsive: true,
|
||||
layout: {
|
||||
topStart: {
|
||||
pageLength: {
|
||||
menu: [10, 25, 50, 100, { label: "All", value: -1 }],
|
||||
},
|
||||
buttons: [
|
||||
{
|
||||
extend: "colvis",
|
||||
columns: "th:not(:first-child):not(:last-child)",
|
||||
text: "Columns",
|
||||
className: "btn btn-sm btn-outline-primary",
|
||||
columnText: function (dt, idx, title) {
|
||||
return idx + 1 + ". " + title;
|
||||
},
|
||||
},
|
||||
{
|
||||
extend: "colvisRestore",
|
||||
text: "Reset",
|
||||
className: "btn btn-sm btn-outline-primary",
|
||||
},
|
||||
{
|
||||
extend: "collection",
|
||||
text: "Export",
|
||||
className: "btn btn-sm btn-outline-primary",
|
||||
buttons: [
|
||||
{
|
||||
extend: "copy",
|
||||
text: "Copy current page",
|
||||
exportOptions: {
|
||||
modifier: {
|
||||
page: "current",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
extend: "csv",
|
||||
bom: true,
|
||||
filename: "bw_instances",
|
||||
exportOptions: {
|
||||
modifier: {
|
||||
search: "none",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
extend: "excel",
|
||||
filename: "bw_instances",
|
||||
exportOptions: {
|
||||
modifier: {
|
||||
search: "none",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
extend: "create_instance",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
$("#instance-form").on("submit", function (event) {
|
||||
event.preventDefault(); // Prevent the default form submission
|
||||
instances_table.on("mouseenter", "td", function () {
|
||||
const colIdx = instances_table.cell(this).index().column;
|
||||
|
||||
const form = $(this);
|
||||
const clickedButton = form.find('button[type="submit"]:focus'); // Find the button that triggered the submit
|
||||
const action = clickedButton.data("action"); // Get the action from the button
|
||||
instances_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each((el) => el.classList.remove("highlight"));
|
||||
|
||||
instances_table
|
||||
.column(colIdx)
|
||||
.nodes()
|
||||
.each((el) => el.classList.add("highlight"));
|
||||
});
|
||||
|
||||
$(document).on("click", "button[data-action]", function () {
|
||||
const form = $(this).closest("form");
|
||||
const action = $(this).data("action"); // Get the action from the button
|
||||
const actionSplit = form.attr("action").split("/");
|
||||
const instanceHostname = actionSplit[actionSplit.length - 1];
|
||||
|
||||
if (
|
||||
action === "delete" &&
|
||||
$(`#method-${instanceHostname}`).val() !== "ui"
|
||||
$(`#method-${instanceHostname}`).text() !== "ui"
|
||||
) {
|
||||
return;
|
||||
} else if ($(`#status-${instanceHostname}`).val() !== "Up") {
|
||||
} else if ($(`#status-${instanceHostname}`).text() !== "Up") {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
6
src/ui/app/static/libs/hammer/hammer.min.js
vendored
6
src/ui/app/static/libs/hammer/hammer.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -31,12 +31,19 @@
|
|||
<link rel="stylesheet"
|
||||
href="libs/perfect-scrollbar/perfect-scrollbar.css"
|
||||
nonce="{{ style_nonce }}" />
|
||||
<link rel="stylesheet"
|
||||
href="libs/apexcharts/apexcharts.css"
|
||||
nonce="{{ style_nonce }}" />
|
||||
<link rel="stylesheet"
|
||||
href="libs/datatables/datatables.min.css"
|
||||
nonce="{{ style_nonce }}" />
|
||||
{% if current_endpoint == "home" %}
|
||||
<link rel="stylesheet"
|
||||
href="libs/apexcharts/apexcharts.css"
|
||||
nonce="{{ style_nonce }}" />
|
||||
{% endif %}
|
||||
{% if current_endpoint == "instances" %}
|
||||
<link rel="stylesheet"
|
||||
href="libs/datatables/datatables.min.css"
|
||||
nonce="{{ style_nonce }}" />
|
||||
<link rel="stylesheet"
|
||||
href="css/pages/instances.css"
|
||||
nonce="{{ style_nonce }}" />
|
||||
{% endif %}
|
||||
<!-- Page CSS -->
|
||||
<!-- Page -->
|
||||
{% if current_endpoint == "login" or current_endpoint == "totp" %}
|
||||
|
|
@ -68,19 +75,21 @@
|
|||
nonce="{{ script_nonce }}"></script>
|
||||
<script src="libs/perfect-scrollbar/perfect-scrollbar.min.js"
|
||||
nonce="{{ script_nonce }}"></script>
|
||||
<script src="libs/hammer/hammer.min.js" nonce="{{ script_nonce }}"></script>
|
||||
<script src="libs/masonry/masonry.pkgd.min.js" nonce="{{ script_nonce }}"></script>
|
||||
<script src="libs/purify/purify.min.js" nonce="{{ script_nonce }}"></script>
|
||||
<script src="libs/datatables/datatables.min.js" nonce="{{ script_nonce }}"></script>
|
||||
{% if current_endpoint == "instances" %}
|
||||
<script src="libs/datatables/datatables.min.js" nonce="{{ script_nonce }}"></script>
|
||||
{% endif %}
|
||||
<!-- Core JS -->
|
||||
<script src="js/menu.js" nonce="{{ script_nonce }}"></script>
|
||||
<script src="libs/apexcharts/apexcharts.min.js" nonce="{{ script_nonce }}"></script>
|
||||
{% if current_endpoint == "home" %}
|
||||
<script src="libs/apexcharts/apexcharts.min.js" nonce="{{ script_nonce }}"></script>
|
||||
<script src="js/dashboards-analytics.js" nonce="{{ script_nonce }}"></script>
|
||||
{% endif %}
|
||||
<!-- Main JS -->
|
||||
<script src="js/main.js" nonce="{{ script_nonce }}"></script>
|
||||
{% if current_endpoint != "login" and current_endpoint != "totp" %}
|
||||
<script async defer src="js/sidebar.js" nonce="{{ script_nonce }}"></script>
|
||||
{% endif %}
|
||||
<script src="js/dashboards-analytics.js" nonce="{{ script_nonce }}"></script>
|
||||
<!-- Page JS -->
|
||||
{% if current_endpoint == "profile" %}
|
||||
<script src="js/pages/profile.js" nonce="{{ script_nonce }}"></script>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
rel="noopener"
|
||||
class="footer-link">Bunkerity</a>
|
||||
</div>
|
||||
<div class="d-none d-lg-inline-block">
|
||||
<div class="d-none d-md-inline-block">
|
||||
<a href="https://docs.bunkerweb.io/latest/?utm_campaign=self&utm_source=ui"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{% extends "dashboard.html" %}
|
||||
{% block content %}
|
||||
<!-- Content -->
|
||||
<div class="card table-responsive text-nowrap p-4 mh-100">
|
||||
<div class="card table-responsive text-nowrap p-4 h-70">
|
||||
<table id="instances" class="table w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
@ -34,13 +34,13 @@
|
|||
{% endif %}
|
||||
</td>
|
||||
<td>{{ instance.type }}</td>
|
||||
<td>{{ instance.creation_date.astimezone().strftime("%Y-%m-%d at %H:%M:%S %Z") }}</td>
|
||||
<td>{{ instance.last_seen.astimezone().strftime("%Y-%m-%d at %H:%M:%S %Z") }}</td>
|
||||
<td>{{ instance.creation_date.astimezone().strftime("%Y-%m-%d %H:%M:%S %Z") }}</td>
|
||||
<td>{{ instance.last_seen.astimezone().strftime("%Y-%m-%d %H:%M:%S %Z") }}</td>
|
||||
<td>
|
||||
<form id="instance-form" action="{{ url_for('instances') }}/{{ instance.hostname }}" method="POST">
|
||||
<input type="hidden"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token() }}" />
|
||||
<form id="instance-form"
|
||||
action="{{ url_for("instances") }}/{{ instance.hostname }}"
|
||||
method="POST">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
{% if instance.status != "up" %}
|
||||
{% set disabled = "disabled" %}
|
||||
{% else %}
|
||||
|
|
@ -51,18 +51,40 @@
|
|||
{% else %}
|
||||
{% set can_delete = "disabled" %}
|
||||
{% endif %}
|
||||
<button type="submit"
|
||||
data-action="ping"
|
||||
class="btn btn-sm btn-outline-primary {{ disabled }}">Ping</button>
|
||||
<button type="submit"
|
||||
data-action="reload"
|
||||
class="btn btn-sm btn-outline-warning {{ disabled }}">Reload</button>
|
||||
<button type="submit"
|
||||
data-action="stop"
|
||||
class="btn btn-sm btn-outline-dark {{ disabled }}">Stop</button>
|
||||
<button type="submit"
|
||||
data-action="delete"
|
||||
class="btn btn-sm btn-outline-danger {{ can_delete }}">Delete</button>
|
||||
<div class="btn-group" role="group" aria-label="Action group">
|
||||
<button type="button"
|
||||
data-action="ping"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
data-bs-original-title="Ping"
|
||||
class="btn btn-sm btn-outline-primary {{ disabled }}">
|
||||
<i class="tf-icons bx bx-xs bx-bell"></i>
|
||||
</button>
|
||||
<button type="button"
|
||||
data-action="stop"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
data-bs-original-title="Stop"
|
||||
class="btn btn-sm btn-outline-primary {{ disabled }}">
|
||||
<i class="tf-icons bx bx-xs bx-stop"></i>
|
||||
</button>
|
||||
<button type="button"
|
||||
data-action="reload"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
data-bs-original-title="Reload"
|
||||
class="btn btn-sm btn-outline-primary {{ disabled }}">
|
||||
<i class="tf-icons bx bx-xs bx-refresh"></i>
|
||||
</button>
|
||||
<button type="button"
|
||||
data-action="delete"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
data-bs-original-title="Delete"
|
||||
class="btn btn-sm btn-outline-danger {{ can_delete }}">
|
||||
<i class="tf-icons bx bx-xs bx-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
@ -70,5 +92,52 @@
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="modal fade"
|
||||
id="modal-create-instance"
|
||||
data-bs-backdrop="static"
|
||||
tabindex="-1"
|
||||
aria-hidden="true"
|
||||
role="dialog">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Create new instance</h5>
|
||||
<button type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"></button>
|
||||
</div>
|
||||
<form action="{{ url_for("instances") }}/new" method="POST">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<div class="mb-3">
|
||||
<label for="hostname" class="form-label">Hostname</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
id="hostname"
|
||||
name="hostname"
|
||||
placeholder="http://bunkerweb"
|
||||
maxlength="256"
|
||||
required />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Name</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
id="name"
|
||||
name="name"
|
||||
placeholder="My Bunker"
|
||||
maxlength="256"
|
||||
required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer justify-content-center">
|
||||
<button type="submit" class="btn btn-outline-primary me-2">Create Instance</button>
|
||||
<button type="reset" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- / Content -->
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
</div>
|
||||
{% if message %}
|
||||
<div class="layout-main-info">
|
||||
<h2>{{ message }}</h2>
|
||||
<h3>{{ message }}</h3>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -477,7 +477,7 @@
|
|||
tabindex="-1"
|
||||
aria-hidden="true"
|
||||
role="dialog">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Recovery Codes</h5>
|
||||
|
|
|
|||
|
|
@ -131,14 +131,15 @@ def inject_variables():
|
|||
if not changes_ongoing and DATA.get("PRO_LOADING"):
|
||||
DATA["PRO_LOADING"] = False
|
||||
|
||||
if not changes_ongoing and metadata["failover"]:
|
||||
flash(
|
||||
"The last changes could not be applied because it creates a configuration error on NGINX, please check the logs for more information. The configured fell back to the last working one.",
|
||||
"error",
|
||||
)
|
||||
elif not changes_ongoing and not metadata["failover"] and DATA.get("CONFIG_CHANGED", False):
|
||||
flash("The last changes have been applied successfully.", "success")
|
||||
DATA["CONFIG_CHANGED"] = False
|
||||
if not request.path.startswith("/loading"):
|
||||
if not changes_ongoing and metadata["failover"]:
|
||||
flash(
|
||||
"The last changes could not be applied because it creates a configuration error on NGINX, please check the logs for more information. The configured fell back to the last working one.",
|
||||
"error",
|
||||
)
|
||||
elif not changes_ongoing and not metadata["failover"] and DATA.get("CONFIG_CHANGED", False):
|
||||
flash("The last changes have been applied successfully.", "success")
|
||||
DATA["CONFIG_CHANGED"] = False
|
||||
|
||||
services = BW_CONFIG.get_config(global_only=True, methods=False, filtered_settings=("SERVER_NAME"))["SERVER_NAME"].split(" ")
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue