Compare commits
16 Commits
97221bc1b7
...
6971548589
| Author | SHA1 | Date | |
|---|---|---|---|
|
6971548589
|
|||
|
31c51574f7
|
|||
|
b652db930f
|
|||
|
1dfddd2fc7
|
|||
|
d20ff9be6e
|
|||
|
2bbe118de6
|
|||
|
840d9ce3c1
|
|||
|
9971981f66
|
|||
|
29b4f3a2ff
|
|||
|
69bf4f1358
|
|||
|
0580a6be53
|
|||
|
563dc62624
|
|||
|
bb229dc724
|
|||
|
5510e9dd08
|
|||
|
3077a98d6f
|
|||
|
1e02da1851
|
@@ -62,7 +62,7 @@ class BackendServer:
|
|||||||
)
|
)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
def _send_ack(self, client: Client, target_id: str) -> None:
|
def send_ack(self, client: Client, target_id: str) -> None:
|
||||||
"""Send an ACK message to a client.
|
"""Send an ACK message to a client.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -73,6 +73,16 @@ class BackendServer:
|
|||||||
self.logger.info(f"[>] Sending ACK to {client}")
|
self.logger.info(f"[>] Sending ACK to {client}")
|
||||||
client.outbound += ack
|
client.outbound += ack
|
||||||
|
|
||||||
|
def send_close(self, client: Client) -> None:
|
||||||
|
"""Send a CLOSE message to a client.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
client (Client): The client to send the CLOSE message to.
|
||||||
|
"""
|
||||||
|
close_msg: bytes = Message.close().to_bytes()
|
||||||
|
self.logger.info(f"[>] Sending CLOSE to {client}")
|
||||||
|
client.outbound += close_msg
|
||||||
|
|
||||||
def _accept_connection(self, sock: socket.socket) -> None:
|
def _accept_connection(self, sock: socket.socket) -> None:
|
||||||
"""Accept a new client connection.
|
"""Accept a new client connection.
|
||||||
|
|
||||||
@@ -96,7 +106,6 @@ class BackendServer:
|
|||||||
sock (socket.socket): The client socket to disconnect.
|
sock (socket.socket): The client socket to disconnect.
|
||||||
"""
|
"""
|
||||||
self.logger.info(f"[-] Disconnecting {client}...")
|
self.logger.info(f"[-] Disconnecting {client}...")
|
||||||
self.logger.debug("[*] Sending DNR message...")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.selector.unregister(client.socket)
|
self.selector.unregister(client.socket)
|
||||||
@@ -155,71 +164,67 @@ class BackendServer:
|
|||||||
try:
|
try:
|
||||||
if mask & selectors.EVENT_READ:
|
if mask & selectors.EVENT_READ:
|
||||||
self._receive_inbound(sock, client)
|
self._receive_inbound(sock, client)
|
||||||
if client.inbound:
|
if not client.inbound:
|
||||||
if client.mac_id is None:
|
|
||||||
# expect HELLO message
|
|
||||||
try:
|
|
||||||
msg = Message.from_bytes(client.inbound)
|
|
||||||
if (
|
|
||||||
msg.category == Category.CONTROL
|
|
||||||
and msg.action == ControlAction.HELLO
|
|
||||||
and msg.payload.get("mac") is not None
|
|
||||||
):
|
|
||||||
client.mac_id = msg.payload["mac"]
|
|
||||||
if (
|
|
||||||
client.mac_id in self.clients
|
|
||||||
and self.clients[client.mac_id].status
|
|
||||||
== "connected"
|
|
||||||
):
|
|
||||||
old_client: Client = self.clients[
|
|
||||||
client.mac_id
|
|
||||||
]
|
|
||||||
self.logger.warning(
|
|
||||||
f"Client {client.mac_id} is already connected from {old_client.addr}, disconnecting old client..."
|
|
||||||
)
|
|
||||||
self._disconnect(old_client)
|
|
||||||
# TODO: tell client not to reconnect
|
|
||||||
self.clients[client.mac_id] = client
|
|
||||||
self.logger.info(
|
|
||||||
f"[+] Registered new client {client}"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.logger.error(
|
|
||||||
f"Expected HELLO message from {client}, got {msg}"
|
|
||||||
)
|
|
||||||
self._disconnect(client)
|
|
||||||
return
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(
|
|
||||||
f"Failed to parse HELLO message from {client}: {e}"
|
|
||||||
)
|
|
||||||
self._disconnect(client)
|
|
||||||
return
|
|
||||||
|
|
||||||
while b"\n" in client.inbound:
|
|
||||||
line, client.inbound = client.inbound.split(b"\n", 1)
|
|
||||||
self.logger.debug(
|
|
||||||
f"[<] Complete message from {client}: {line!r}"
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
msg = Message.from_bytes(line)
|
|
||||||
self.logger.info(f"[.] Parsed message {msg.id}")
|
|
||||||
if msg.ack_required:
|
|
||||||
self._send_ack(client, target_id=msg.id)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(
|
|
||||||
f"Failed to parse message from {client}: {e}"
|
|
||||||
)
|
|
||||||
self._disconnect(client)
|
|
||||||
return
|
|
||||||
|
|
||||||
else:
|
|
||||||
self._disconnect(client)
|
self._disconnect(client)
|
||||||
|
return
|
||||||
|
|
||||||
if mask & selectors.EVENT_WRITE:
|
if client.mac_id is None:
|
||||||
if client.outbound:
|
# expect HELLO message
|
||||||
self._send_outbound(sock, client)
|
try:
|
||||||
|
msg = Message.from_bytes(client.inbound)
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(
|
||||||
|
f"Failed to parse HELLO message from {client}: {e}"
|
||||||
|
)
|
||||||
|
self._disconnect(client)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (
|
||||||
|
msg.category == Category.CONTROL
|
||||||
|
and msg.action == ControlAction.HELLO
|
||||||
|
and msg.payload.get("mac") is not None
|
||||||
|
):
|
||||||
|
client.mac_id = msg.payload["mac"]
|
||||||
|
if (
|
||||||
|
client.mac_id in self.clients
|
||||||
|
and self.clients[client.mac_id].status
|
||||||
|
== "connected"
|
||||||
|
):
|
||||||
|
old_client: Client = self.clients[client.mac_id]
|
||||||
|
self.logger.warning(
|
||||||
|
f"Client {client.mac_id} is already connected from {old_client.addr}, disconnecting old client..."
|
||||||
|
)
|
||||||
|
self.send_close(old_client)
|
||||||
|
# self._disconnect(old_client)
|
||||||
|
# TODO: tell client not to reconnect
|
||||||
|
self.clients[client.mac_id] = client
|
||||||
|
self.logger.info(f"[+] Registered new client {client}")
|
||||||
|
else:
|
||||||
|
self.logger.error(
|
||||||
|
f"Expected HELLO message from {client}, got {msg}"
|
||||||
|
)
|
||||||
|
self._disconnect(client)
|
||||||
|
return
|
||||||
|
|
||||||
|
while b"\n" in client.inbound:
|
||||||
|
line, client.inbound = client.inbound.split(b"\n", 1)
|
||||||
|
self.logger.debug(
|
||||||
|
f"[<] Complete message from {client}: {line!r}"
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
msg = Message.from_bytes(line)
|
||||||
|
self.logger.info(f"[.] Parsed message {msg.id}")
|
||||||
|
if msg.ack_required:
|
||||||
|
self.send_ack(client, target_id=msg.id)
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(
|
||||||
|
f"Failed to parse message from {client}: {e}"
|
||||||
|
)
|
||||||
|
self._disconnect(client)
|
||||||
|
return
|
||||||
|
|
||||||
|
if mask & selectors.EVENT_WRITE and client.outbound:
|
||||||
|
self._send_outbound(sock, client)
|
||||||
|
|
||||||
except ConnectionResetError as e:
|
except ConnectionResetError as e:
|
||||||
self.logger.error(f"Connection reset by {client}, disconnect: {e}")
|
self.logger.error(f"Connection reset by {client}, disconnect: {e}")
|
||||||
|
|||||||
31
src/judas_server/web/routes/client_details.py
Normal file
31
src/judas_server/web/routes/client_details.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
import flask
|
||||||
|
import flask_login
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from judas_server.backend import BackendServer
|
||||||
|
from judas_server.backend.client import Client
|
||||||
|
|
||||||
|
bp: flask.Blueprint = flask.Blueprint(
|
||||||
|
"client_details", __name__, url_prefix="/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/<client_id>")
|
||||||
|
@flask_login.login_required
|
||||||
|
def client_details(client_id: str) -> str:
|
||||||
|
"""Renders the client details page for a specific client.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
client_id: The ID of the client to display details for.
|
||||||
|
"""
|
||||||
|
backend: BackendServer = flask.current_app.config["BACKEND"]
|
||||||
|
client: Client | None = backend.clients.get(client_id)
|
||||||
|
|
||||||
|
if not client:
|
||||||
|
flask.abort(404, description="Client not found")
|
||||||
|
|
||||||
|
return flask.render_template("client_details.html", client=client)
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
@import url("https://cdn-uicons.flaticon.com/2.6.0/uicons-regular-rounded/css/uicons-regular-rounded.css");
|
||||||
|
@import url("https://cdn-uicons.flaticon.com/2.6.0/uicons-regular-straight/css/uicons-regular-straight.css");
|
||||||
|
@import url("https://cdn-uicons.flaticon.com/2.6.0/uicons-solid-rounded/css/uicons-solid-rounded.css");
|
||||||
|
@import url("https://cdn-uicons.flaticon.com/2.6.0/uicons-solid-straight/css/uicons-solid-straight.css");
|
||||||
|
@import url("https://cdn-uicons.flaticon.com/2.6.0/uicons-bold-rounded/css/uicons-bold-rounded.css");
|
||||||
|
|
||||||
@import "palette.css";
|
@import "palette.css";
|
||||||
|
|
||||||
* {
|
* {
|
||||||
@@ -12,6 +18,10 @@ body {
|
|||||||
color: var(--ctp-text);
|
color: var(--ctp-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fi {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
color: #eceff4;
|
color: #eceff4;
|
||||||
@@ -38,16 +48,26 @@ header {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: var(--ctp-mantle);
|
background-color: var(--ctp-mantle);
|
||||||
/* color: var(--ctp-text); */
|
/* color: var(--ctp-text); */
|
||||||
padding: 1rem;
|
padding: 0.5rem 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside {
|
||||||
|
width: 20rem;
|
||||||
|
background-color: var(--ctp-base);
|
||||||
|
border-right: 2px solid var(--ctp-mantle);
|
||||||
|
}
|
||||||
|
|
||||||
|
#content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
padding: 1rem;
|
|
||||||
/* background-color: var(--ctp-base); */
|
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
header a {
|
header a {
|
||||||
@@ -57,13 +77,12 @@ header a {
|
|||||||
|
|
||||||
.button {
|
.button {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 0.5rem 1rem;
|
padding: 0.25rem 0.5rem;
|
||||||
background-color: var(--ctp-lavender);
|
background-color: var(--ctp-lavender);
|
||||||
color: var(--ctp-base);
|
color: var(--ctp-base);
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: 0.3s ease-in-out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* .button a:hover { */
|
/* .button a:hover { */
|
||||||
@@ -76,6 +95,10 @@ header a {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button:active {
|
||||||
|
background-color: var(--ctp-sapphire);
|
||||||
|
}
|
||||||
|
|
||||||
.center {
|
.center {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
@@ -200,3 +223,31 @@ header a {
|
|||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#no-connection-icon {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
vertical-align: middle;
|
||||||
|
color: var(--ctp-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul#client-list {
|
||||||
|
list-style: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul#client-list li {
|
||||||
|
padding: 0 0.5rem;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul#client-list li:hover {
|
||||||
|
background-color: var(--ctp-surface0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul#client-list li a {
|
||||||
|
display: block;
|
||||||
|
color: var(--ctp-text);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: 0.1s ease-in-out;
|
||||||
|
}
|
||||||
|
|||||||
9
src/judas_server/web/templates/client_details.html
Normal file
9
src/judas_server/web/templates/client_details.html
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Details for client {{ client.id }}</title>
|
||||||
|
</head>
|
||||||
|
<body></body>
|
||||||
|
</html>
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>judas panel - details</title>
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
href="{{ url_for('static', filename='css/style.css') }}"
|
|
||||||
/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="wrapper">
|
|
||||||
<header>
|
|
||||||
<h1><a href="{{ url_for('index.index') }}">judas</a></h1>
|
|
||||||
<p><a class="button" href="{{ url_for('logout') }}">Logout</a></p>
|
|
||||||
</header>
|
|
||||||
<main>
|
|
||||||
<div class="button-container">
|
|
||||||
<p><a href="{{ url_for('panel') }}" class="link">Back to Panel</a></p>
|
|
||||||
<h2>{{ pc.id }}</h2>
|
|
||||||
<p>
|
|
||||||
<a href="{{ url_for('stream', pc_id=pc.id) }}" class="link"
|
|
||||||
>View Stream</a
|
|
||||||
>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<table class="center-table details-table">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>Status</td>
|
|
||||||
<td>{{ pc.status }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Last Seen</td>
|
|
||||||
<td>{{ pc.last_seen }}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
<header>
|
<header>
|
||||||
<h1><a href="{{ url_for('index.index') }}">judas</a></h1>
|
<h2><a href="{{ url_for('index.index') }}">judas</a></h2>
|
||||||
{% if logged %}
|
{% if logged %}
|
||||||
<p><a class="button" href="{{ url_for('auth.logout') }}">Logout</a></p>
|
<p><a class="button" href="{{ url_for('auth.logout') }}">Logout</a></p>
|
||||||
{% else %}
|
{% else %}
|
||||||
@@ -17,17 +17,19 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</header>
|
</header>
|
||||||
<main class="center">
|
<main class="center">
|
||||||
<div>
|
<div id="content">
|
||||||
<p>Welcome to</p>
|
<div>
|
||||||
<h2 id="typing-text" style="font-size: 3rem;">judas</h2>
|
<p>Welcome to</p>
|
||||||
<p>a remote PC fleet management system</p>
|
<h2 id="typing-text" style="font-size: 3rem;">judas</h2>
|
||||||
|
<p>a remote PC fleet management system</p>
|
||||||
|
</div>
|
||||||
|
<p style="color: var(--ctp-red);"><strong>Notice:</strong> Please use this system responsibly and in accordance with all applicable laws and organizational policies.</p>
|
||||||
|
{% if logged %}
|
||||||
|
<p><a class="button" href="{{ url_for('panel.panel') }}">Go to panel</a></p>
|
||||||
|
{% else %}
|
||||||
|
<p>Please <a href="{{ url_for('auth.login')}}" class="link">log in</a> to manage your remote PCs.</p>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<p style="color: #bf616a;"><strong>Notice:</strong> Please use this system responsibly and in accordance with all applicable laws and organizational policies.</p>
|
|
||||||
{% if logged %}
|
|
||||||
<p><a class="button" href="{{ url_for('panel.panel') }}">Go to panel</a></p>
|
|
||||||
{% else %}
|
|
||||||
<p>Please <a href="{{ url_for('auth.login')}}" class="link">log in</a> to manage your remote PCs.</p>
|
|
||||||
{% endif %}
|
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -9,23 +9,25 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
<header>
|
<header>
|
||||||
<h1><a href="{{ url_for('index.index') }}">judas</a></h1>
|
<h2><a href="{{ url_for('index.index') }}">judas</a></h2>
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<h1>Login</h1>
|
<div id="content">
|
||||||
<form method="post" class="center">
|
<h1>Login</h1>
|
||||||
<label for="password">Password:</label>
|
<form method="post" class="center">
|
||||||
<input type="password" id="password" name="password" required autofocus>
|
<label for="password">Password:</label>
|
||||||
<br>
|
<input type="password" id="password" name="password" required autofocus>
|
||||||
<br>
|
<br>
|
||||||
<input type="submit" value="Login" class="button">
|
<br>
|
||||||
{% if error %}
|
<input type="submit" value="Login" class="button">
|
||||||
<div class="error-container">
|
{% if error %}
|
||||||
<h1>Login failure</h1>
|
<div class="error-container">
|
||||||
<p>{{ error }}</p>
|
<h1>Login failure</h1>
|
||||||
</div>
|
<p>{{ error }}</p>
|
||||||
{% endif %}
|
</div>
|
||||||
</form>
|
{% endif %}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -27,11 +27,13 @@
|
|||||||
|
|
||||||
socket.on("connect", () => {
|
socket.on("connect", () => {
|
||||||
console.log("Connected to server");
|
console.log("Connected to server");
|
||||||
|
$("#no-connection-icon").hide();
|
||||||
showNotify("Connected to server");
|
showNotify("Connected to server");
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("disconnect", () => {
|
socket.on("disconnect", () => {
|
||||||
console.log("Disconnected from server");
|
console.log("Disconnected from server");
|
||||||
|
$("#no-connection-icon").show();
|
||||||
showNotify("Disconnected from server");
|
showNotify("Disconnected from server");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -42,6 +44,26 @@
|
|||||||
null,
|
null,
|
||||||
2,
|
2,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Update client list
|
||||||
|
const clientList = $("#client-list");
|
||||||
|
|
||||||
|
clientList.empty();
|
||||||
|
Object.entries(data).forEach(([clientId, client]) => {
|
||||||
|
const listItem = document.createElement("li");
|
||||||
|
|
||||||
|
const iconElement = document.createElement("i");
|
||||||
|
iconElement.classList.add("fi", "fi-sr-play");
|
||||||
|
iconElement.style.color = "var(--ctp-green)";
|
||||||
|
listItem.appendChild(iconElement);
|
||||||
|
|
||||||
|
const spanElement = document.createElement("a");
|
||||||
|
spanElement.appendChild(document.createTextNode(clientId));
|
||||||
|
spanElement.href = `#${clientId}`;
|
||||||
|
|
||||||
|
listItem.appendChild(spanElement);
|
||||||
|
clientList.append(listItem);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -49,12 +71,22 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
<header>
|
<header>
|
||||||
<h1><a href="{{ url_for('index.index') }}">judas</a></h1>
|
<h2><a href="{{ url_for('index.index') }}">judas</a></h2>
|
||||||
<p><a class="button" href="{{ url_for('auth.logout') }}">Logout</a></p>
|
<p>
|
||||||
|
<span id="no-connection-icon" style="display: none">
|
||||||
|
<i class="fi fi-rr-link-slash"></i>
|
||||||
|
</span>
|
||||||
|
<a class="button" href="{{ url_for('auth.logout') }}">Logout</a>
|
||||||
|
</p>
|
||||||
</header>
|
</header>
|
||||||
<div id="notify"></div>
|
<div id="notify"></div>
|
||||||
<main>
|
<main>
|
||||||
<pre id="data"></pre>
|
<aside>
|
||||||
|
<ul id="client-list"></ul>
|
||||||
|
</aside>
|
||||||
|
<div id="content">
|
||||||
|
<pre id="data"></pre>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -53,12 +53,19 @@ class JudasWebServer:
|
|||||||
|
|
||||||
def init_routes(self) -> None:
|
def init_routes(self) -> None:
|
||||||
self.logger.debug("Initializing routes...")
|
self.logger.debug("Initializing routes...")
|
||||||
from judas_server.web.routes import api, auth_bp, index_bp, panel_bp
|
from judas_server.web.routes import (
|
||||||
|
api,
|
||||||
|
auth_bp,
|
||||||
|
index_bp,
|
||||||
|
panel_bp,
|
||||||
|
client_details,
|
||||||
|
)
|
||||||
|
|
||||||
self.app.register_blueprint(index_bp)
|
self.app.register_blueprint(index_bp)
|
||||||
self.app.register_blueprint(auth_bp)
|
self.app.register_blueprint(auth_bp)
|
||||||
self.app.register_blueprint(panel_bp)
|
self.app.register_blueprint(panel_bp)
|
||||||
self.app.register_blueprint(api.bp)
|
self.app.register_blueprint(api.bp)
|
||||||
|
self.app.register_blueprint(client_details.bp)
|
||||||
api.emit_polled_data(self.app, self.socketio)
|
api.emit_polled_data(self.app, self.socketio)
|
||||||
|
|
||||||
def run(self, host: str = "0.0.0.0", port: int = 5000) -> None:
|
def run(self, host: str = "0.0.0.0", port: int = 5000) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user