30 Commits

Author SHA1 Message Date
5c4441a35f style(style.css): remove empty line 2025-06-16 12:23:26 +02:00
a525f311a3 Merge branch 'feat/improve-login-page' into develop 2025-06-16 12:17:27 +02:00
e07a379036 refactor(web_server.py): display pretty error if password incorrect 2025-06-16 12:14:02 +02:00
ec6ed477b9 feat(login.html): display error if provided 2025-06-16 12:13:11 +02:00
f9e630e49b Merge pull request 'feat: style panel' (#1) from feat/style-panel into develop
Reviewed-on: #1
2025-06-16 12:12:35 +02:00
960a7dd4bf refactor(index.html): put elements in main into divs to not screw up layout 2025-06-16 12:11:11 +02:00
6872348375 feat(panel.html): change bg color based on status 2025-06-16 12:10:39 +02:00
7c98d16cc2 feat(panel.html): add description and classes to table 2025-06-16 12:09:40 +02:00
06ea0246f5 chore(web_server.py): add more pc placeholders 2025-06-16 12:08:21 +02:00
95e40a4128 feat(style.css): add error container class 2025-06-16 12:07:52 +02:00
cde27d37e3 refactor(web_server.py): make status lowercase 2025-06-16 12:07:29 +02:00
3709a42e64 feat(style.css): style panel table 2025-06-16 12:06:42 +02:00
7b09a72d5c feat(style.css): replace colors with var values 2025-06-16 12:05:39 +02:00
1f0fcc71c9 refactor(style.css): make main a flex container 2025-06-16 12:02:35 +02:00
d998b85f9f feat(style.css): make all links pretty 2025-06-16 12:01:37 +02:00
c0f51cbaaf feat(style.css): add nord colors as variables for easy use 2025-06-16 12:01:06 +02:00
f3f521efe2 style(web_server.py): fix type in index() 2025-06-16 10:45:44 +02:00
feb7a71816 refactor(web_server.py): redirect from / to /panel if logged in 2025-06-16 10:45:08 +02:00
e15f02be05 refactor(web_server.py): render login.html instead of bare form in /login 2025-06-16 10:44:00 +02:00
5031799072 feat(login.html): add basic login page 2025-06-16 10:43:18 +02:00
26fac14802 refactor(stream.html): make logout button pretty and fix name in header 2025-06-16 10:39:45 +02:00
75694f9200 refactor(details.html): make logout button pretty and fix name in header 2025-06-16 10:38:54 +02:00
522c14793a refactor(panel.html): make logout button pretty and fix name in header 2025-06-16 10:38:23 +02:00
9a074f17d8 style(index.html): add newline at end of file 2025-06-16 10:37:13 +02:00
6ec1af2cf7 feat(index.html): add pretty typewriter effect to header on main 2025-06-16 10:36:46 +02:00
ff911bc8bc feat(index.html): add welcome text and a disclaimer 2025-06-16 10:36:14 +02:00
446e5fbc04 feat(index.html): add link to homepage to app name in header 2025-06-16 10:34:52 +02:00
04c46f1e98 refactor(style.css): add barebones stylesheet 2025-06-16 10:32:04 +02:00
2786f39b9b chore(.vscode/launch.json): add launch task for flask app 2025-06-16 10:30:16 +02:00
ad479fe0d7 build(pyproject.toml): rename start script to web 2025-06-16 10:28:56 +02:00
9 changed files with 291 additions and 31 deletions

22
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,22 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Flask",
"type": "debugpy",
"request": "launch",
"module": "flask",
"env": {
"FLASK_APP": "judas_server/web/web_server.py",
},
"args": [
"run",
],
"console": "internalConsole",
"justMyCode": true,
}
]
}

View File

@@ -1,20 +1,172 @@
:root {
--nord-bg0: #2e3440;
--nord-bg1: #3b4252;
--nord-bg2: #434c5e;
--nord-bg3: #4c566a;
--nord-fg0: #eceff4;
--nord-fg1: #e5e9f0;
--nord-fg2: #d8dee9;
--nord-acc0: #8fbcbb;
--nord-acc1: #88c0d0;
--nord-acc2: #81a1c1;
--nord-acc3: #5e81ac;
--nord-aur0: #bf616a;
--nord-aur1: #d08770;
--nord-aur2: #ebcb8b;
--nord-aur3: #a3be8c;
--nord-aur4: #b48ead;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: var(--nord-aur4);
font-family: monospace, sans-serif !important;
color: var(--nord-fg0);
}
input {
font-family: inherit;
color: #eceff4;
background-color: var(--nord-bg2);
border: none;
border-radius: 0.25rem;
padding: 0.25rem;
}
a {
color: var(--nord-acc0);
text-decoration: none;
transition: 0.3s ease-in-out;
}
a:hover {
color: var(--nord-acc2);
}
#wrapper {
display: flex;
flex-direction: column;
min-height: 100vh;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #2e3440;
color: #eceff4;
background-color: var(--nord-bg0);
color: var(--nord-fg0);
padding: 1rem;
}
main {
padding: 2rem;
background-color: #eceff4;
color: #2e3440;
display: flex;
flex-direction: column;
gap: 1rem;
padding: 1rem;
background-color: var(--nord-bg1);
flex-grow: 1;
text-align: center;
}
header a {
text-decoration: none;
color: var(--nord-fg0);
}
.button {
display: inline-block;
padding: 0.5rem 1rem;
background-color: #8fbcbb;
color: var(--nord-bg0);
border: none;
border-radius: 0.25rem;
text-decoration: none;
transition: 0.3s ease-in-out;
}
.button:hover {
background-color: var(--nord-acc2);
cursor: pointer;
}
.center {
text-align: center;
}
.error-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin: 1rem;
padding: 1rem;
background-color: var(--nord-aur0);
color: var(--nord-fg0);
border: 6px solid var(--nord-aur1);
border-radius: 24px;
}
.center-table {
margin: 0 auto;
width: 100%;
}
.select-table{
border-collapse: collapse;
border: 2px solid var(--nord-fg0);
}
.select-table thead {
position: sticky;
top: -1px;
z-index: 2;
}
.select-table th, .select-table td {
padding: 0.5rem;
text-align: center;
border: 1px solid var(--nord-fg1);
border-collapse: collapse;
}
.select-table th {
background-color: var(--nord-bg2);
color: var(--nord-fg0);
}
.select-table a {
display: block;
color: var(--nord-acc0);
text-decoration: none;
transition: 0.1s ease-in-out;
}
.select-table tr {
transition: 0.1s ease-in-out;
}
.select-table tr:hover {
background-color: var(--nord-acc1);
}
.select-table tr:hover a {
color: var(--nord-bg0);
}
.red-bg {
background-color: var(--nord-aur0);
}
.yellow-bg {
background-color: var(--nord-aur2);
}
.green-bg {
background-color: var(--nord-aur3);
}

View File

@@ -9,8 +9,8 @@
<body>
<div id="wrapper">
<header>
<h1><a href="{{ url_for('panel') }}">judas panel</a></h1>
<p>Welcome, {{ username }}! <a href="{{ url_for('logout') }}">Logout</a></p>
<h1><a href="{{ url_for('index') }}">judas</a></h1>
<p><a class="button" href="{{ url_for('logout') }}">Logout</a></p>
</header>
<main>
<h2>Details for PC ID: {{ pc.id }}</h2>

View File

@@ -9,13 +9,44 @@
<body>
<div id="wrapper">
<header>
<h1>judas</h1>
<h1><a href="{{ url_for('index') }}">judas</a></h1>
{% if logged %}
<p>Welcome, {{ username }}! <a href="{{ url_for('logout') }}">Logout</a></p>
{% else %}
<p><a href="{{ url_for('login') }}">Login</a></p>
<p><a class="button" href="{{ url_for('login') }}">Login</a></p>
{% endif %}
</header>
<main class="center">
<div>
<p>Welcome to</p>
<h2 id="typing-text" style="font-size: 3rem;">judas</h2>
<p>a remote PC fleet management system</p>
</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>
<p>Please <a href="{{ url_for('login')}}">log in</a> to manage your remote PCs.</p>
</div>
</main>
</div>
<script>
var i = 0;
var txt = document.getElementById("typing-text").innerHTML;
var minSpeed = 50;
var maxSpeed = 200;
document.getElementById("typing-text").innerHTML = "";
function typeWriter() {
if (i < txt.length) {
document.getElementById("typing-text").innerHTML += txt.charAt(i);
i++;
var randomDelay = Math.floor(Math.random() * (maxSpeed - minSpeed + 1)) + minSpeed;
setTimeout(typeWriter, randomDelay);
}
}
typeWriter();
</script>
</body>
</html>
</html>

View File

@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>judas - login page</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
<div id="wrapper">
<header>
<h1><a href="{{ url_for('index') }}">judas</a></h1>
</header>
<main>
<h1>Login</h1>
<form method="post" class="center">
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
<br>
<br>
<input type="submit" value="Login" class="button">
{% if error %}
<div class="error-container">
<h1>Login failure</h1>
<p>{{ error }}</p>
</div>
{% endif %}
</form>
</main>
</div>
</body>
</html>

View File

@@ -9,11 +9,14 @@
<body>
<div id="wrapper">
<header>
<h1><a href="{{ url_for('panel') }}">judas panel</a></h1>
<p>Welcome, {{ username }}! <a href="{{ url_for('logout') }}">Logout</a></p>
<h1><a href="{{ url_for('index') }}">judas</a></h1>
<p><a class="button" href="{{ url_for('logout') }}">Logout</a></p>
</header>
<main>
<table border="1">
<h2 class="center">Select a PC to Control</h2>
<p class="center">Choose a PC from the list below to view details or send commands.</p>
<br>
<table class="center-table select-table">
<thead>
<tr>
<th>PC ID</th>
@@ -25,8 +28,7 @@
{% for pc in pcs.values() %}
<tr>
<td><a href="{{ url_for('details', pc_id=pc.id) }}">{{ pc.id }}</a></td>
<td>{{ pc.status if pc.status else 'Unknown' }}</td>
<td>{{ pc.last_seen if pc.last_seen else 'Never' }}</td>
<td class='{% if pc.status == "online" %}green-bg{% elif pc.status == "offline" %}red-bg{% else %}yellow-bg{% endif %}'>{{ pc.status if pc.status else 'Unknown' }}</td> <td>{{ pc.last_seen if pc.last_seen else 'Never' }}</td>
</tr>
{% else %}
<tr>

View File

@@ -8,8 +8,8 @@
<body>
<div id="wrapper">
<header>
<h1><a href="{{ url_for('panel') }}">judas panel</a></h1>
<p>Welcome, {{ username }}! <a href="{{ url_for('logout') }}">Logout</a></p>
<h1><a href="{{ url_for('index') }}">judas</a></h1>
<p><a class="button" href="{{ url_for('logout') }}">Logout</a></p>
</header>
</div>
</body>

View File

@@ -17,14 +17,39 @@ login_manager.init_app(app)
PC_DETAILS = {
"PC1": {
"id": "PC1",
"status": "Online",
"status": "online",
"last_seen": "2023-10-01 12:00:00",
},
"PC2": {
"id": "PC2",
"status": "Offline",
"status": "offline",
"last_seen": "2023-10-01 11:00:00",
},
"PC3": {
"id": "PC3",
"status": "offline",
"last_seen": "2023-10-01 11:00:00",
},
"PC4": {
"id": "PC4",
"status": "offline",
"last_seen": "2023-10-01 11:00:00",
},
"PC5": {
"id": "PC5",
"status": "offline",
"last_seen": "2023-10-01 11:00:00",
},
"PC6": {
"id": "PC6",
"status": "offline",
"last_seen": "2023-10-01 11:00:00",
},
"PC7": {
"id": "PC7",
"status": "offline",
"last_seen": "2023-10-01 11:00:00",
}
}
@@ -52,12 +77,10 @@ def load_user(user_id: str) -> User | None:
@app.route("/")
def index() -> str:
def index() -> flask.Response | str:
"""Renders the index page with a link to the login page."""
if flask_login.current_user.is_authenticated:
return flask.render_template(
"index.html", logged=True, username=flask_login.current_user.id
)
return flask.redirect(flask.url_for("panel"))
else:
return flask.render_template(
"index.html", logged=False, login_url=flask.url_for("login")
@@ -74,13 +97,11 @@ def login() -> str:
flask_login.login_user(user)
return flask.redirect(flask.url_for("panel"))
else:
return "Invalid password", 401
return """
<form method="post">
Password: <input type="password" name="password">
<input type="submit" value="Login">
</form>
"""
return flask.render_template(
"login.html",
error="Invalid password. Please try again.",
)
return flask.render_template("login.html")
@app.route("/logout")

View File

@@ -11,7 +11,7 @@ readme = "README.md"
license = {text = "GPL-3.0"}
[tool.pdm.scripts]
start = "flask --app judas_server/web/web_server.py run"
web = "flask --app judas_server/web/web_server.py run"
[tool.pdm]
distribution = false