1 Commits

Author SHA1 Message Date
Tyler 8f2d56c05a Better UI for both Desktop and Mobile 2025-05-14 15:31:43 -10:00
17 changed files with 527 additions and 467 deletions
+35 -58
View File
@@ -6,11 +6,11 @@ import html
import base64 import base64
import hashlib import hashlib
import secrets import secrets
import datetime
import subprocess import subprocess
import platform import platform
from datetime import UTC from datetime import datetime, timedelta
import sys import sys
import psutil
# ===== Third-Party Imports ===== # ===== Third-Party Imports =====
from flask import ( from flask import (
@@ -138,7 +138,7 @@ def log_admin_event(message: str):
try: try:
key = load_admin_key() key = load_admin_key()
cipher = Fernet(key) cipher = Fernet(key)
timestamp = datetime.datetime.now(UTC).strftime("%Y-%m-%d %H:%M:%S") timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
encrypted = cipher.encrypt(f"[{timestamp}] {message}".encode()) encrypted = cipher.encrypt(f"[{timestamp}] {message}".encode())
with open(ADMIN_LOG_FILE, 'ab') as f: with open(ADMIN_LOG_FILE, 'ab') as f:
f.write(encrypted + b"\n") f.write(encrypted + b"\n")
@@ -149,12 +149,12 @@ def log_admin_event(message: str):
def cleanup_expired_files(): def cleanup_expired_files():
"""Remove files older than MAX_FILE_AGE_DAYS.""" """Remove files older than MAX_FILE_AGE_DAYS."""
try: try:
now = datetime.datetime.now(UTC) now = datetime.now()
for fname in os.listdir(UPLOAD_FOLDER): for fname in os.listdir(UPLOAD_FOLDER):
if fname.endswith(".enc") or fname.endswith(".json"): if fname.endswith(".enc") or fname.endswith(".json"):
path = os.path.join(UPLOAD_FOLDER, fname) path = os.path.join(UPLOAD_FOLDER, fname)
try: try:
file_time = datetime.datetime.fromtimestamp(os.path.getmtime(path), UTC) file_time = datetime.datetime.fromtimestamp(os.path.getmtime(path), )
age = (now - file_time).days age = (now - file_time).days
if age > MAX_FILE_AGE_DAYS: if age > MAX_FILE_AGE_DAYS:
os.remove(path) os.remove(path)
@@ -208,7 +208,7 @@ def handle_file_upload(request):
meta = { meta = {
'pickup_password': base64.urlsafe_b64encode(hashlib.sha256(pickup_password.encode()).digest()).decode(), 'pickup_password': base64.urlsafe_b64encode(hashlib.sha256(pickup_password.encode()).digest()).decode(),
'original_name': filename, 'original_name': filename,
'timestamp': datetime.datetime.now(UTC).isoformat() 'timestamp': datetime.datetime.now().isoformat()
} }
with open(os.path.join(UPLOAD_FOLDER, f"{random_id}.json"), 'w') as f: with open(os.path.join(UPLOAD_FOLDER, f"{random_id}.json"), 'w') as f:
json.dump(meta, f) json.dump(meta, f)
@@ -408,48 +408,30 @@ def admin_page():
cleanup_expired_files() cleanup_expired_files()
routes = [rule.rule for rule in app.url_map.iter_rules() if rule.endpoint != 'static'] routes = [rule.rule for rule in app.url_map.iter_rules() if rule.endpoint != 'static']
# Get uptime based on OS now = datetime.now()
if platform.system() == "Windows": try:
try: boot_time = datetime.fromtimestamp(psutil.boot_time())
# Windows uptime using PowerShell
ps_command = "(Get-CimInstance -ClassName Win32_OperatingSystem).LastBootUpTime" uptime = now - boot_time
uptime_output = subprocess.check_output(["powershell", "-Command", ps_command], shell=True).decode() days = uptime.days
# Convert the PowerShell DateTime to Python datetime hours, remainder = divmod(uptime.seconds, 3600)
boot_time = datetime.datetime.strptime(uptime_output.strip(), "%A, %B %d, %Y %I:%M:%S %p") minutes = remainder // 60
# Make boot_time timezone-aware (assuming local time) uptime_str = f"{days} days, {hours} hours, {minutes} minutes"
boot_time = boot_time.replace(tzinfo=datetime.timezone.utc) except Exception as e:
current_time = datetime.datetime.now(UTC) print(f"[ERROR] Uptime calculation failed: {e}")
uptime = current_time - boot_time uptime_str = "Unavailable"
uptime_str = f"{uptime.days} days, {uptime.seconds // 3600} hours, {(uptime.seconds % 3600) // 60} minutes"
except Exception as e:
print(f"[ERROR] Failed to get Windows uptime: {str(e)}")
uptime_str = "Unavailable"
else:
try:
# Try reading from /proc/uptime first
with open('/proc/uptime', 'r') as f:
uptime_seconds = float(f.readline().split()[0])
days = int(uptime_seconds // 86400)
hours = int((uptime_seconds % 86400) // 3600)
minutes = int((uptime_seconds % 3600) // 60)
uptime_str = f"{days} days, {hours} hours, {minutes} minutes"
except Exception:
try:
# Fallback to uptime command if /proc/uptime fails
uptime_str = subprocess.check_output("uptime -p", shell=True).decode().strip()
except Exception as e:
print(f"[ERROR] Failed to get Linux uptime: {str(e)}")
uptime_str = "Unavailable"
server_info = { server_info = {
"uptime": uptime_str, "uptime": uptime_str,
"time": datetime.datetime.now(UTC).strftime("%Y-%m-%d %H:%M:%S"), "server_time": now.strftime("%Y-%m-%d %H:%M:%S"),
"python": platform.python_version(), "python_version": platform.python_version(),
"debug": app.debug "debug_mode": app.debug
} }
return render_template("admin.html", routes=routes, server_info=server_info) return render_template("admin.html", routes=routes, server_info=server_info)
@app.route("/restart-server", methods=["POST"]) @app.route("/restart-server", methods=["POST"])
def restart_server(): def restart_server():
"""Restart the server.""" """Restart the server."""
@@ -458,9 +440,7 @@ def restart_server():
try: try:
if platform.system() == "Windows": if platform.system() == "Windows":
# Get the current process ID
current_pid = os.getpid() current_pid = os.getpid()
# Create a batch file to restart the server
restart_script = f""" restart_script = f"""
@echo off @echo off
timeout /t 2 /nobreak timeout /t 2 /nobreak
@@ -470,33 +450,30 @@ def restart_server():
""" """
with open("restart.bat", "w") as f: with open("restart.bat", "w") as f:
f.write(restart_script) f.write(restart_script)
# Start the restart script and exit
subprocess.Popen(["restart.bat"], shell=True) subprocess.Popen(["restart.bat"], shell=True)
return jsonify({"message": "Server restart initiated"}), 200 return jsonify({"message": "Server restart initiated"}), 200
else: else:
# For Linux/Unix systems, use a Python-based restart current_pid = os.getpid()
# Get the current Python interpreter and script path
python_path = sys.executable python_path = sys.executable
script_path = os.path.abspath(__file__) script_path = os.path.abspath(__file__)
current_pid = os.getpid()
# Create a shell script to restart the server # Create a safer and cleaner restart script
restart_script = f"""#!/bin/bash restart_script = """#!/bin/bash
sleep 2 sleep 2
kill -9 {current_pid} PID=$1
export PRODUCTION=true kill "$PID"
{python_path} {script_path} while kill -0 "$PID" 2>/dev/null; do sleep 0.5; done
""" export PRODUCTION=true
exec "$2" "$3"
"""
# Write and make the script executable
with open("restart.sh", "w") as f: with open("restart.sh", "w") as f:
f.write(restart_script) f.write(restart_script)
os.chmod("restart.sh", 0o755) os.chmod("restart.sh", 0o755)
# Start the restart script and exit subprocess.Popen(["./restart.sh", str(current_pid), python_path, script_path])
subprocess.Popen(["./restart.sh"], shell=True)
return jsonify({"message": "Server restart initiated"}), 200 return jsonify({"message": "Server restart initiated"}), 200
except Exception as e: except Exception as e:
print(f"[ERROR] Failed to restart server: {str(e)}") print(f"[ERROR] Failed to restart server: {str(e)}")
return jsonify({"error": f"Failed to restart server: {str(e)}"}), 500 return jsonify({"error": f"Failed to restart server: {str(e)}"}), 500
+1
View File
@@ -4,6 +4,7 @@ flask==3.0.3
cryptography==42.0.5 cryptography==42.0.5
waitress==2.1.2 waitress==2.1.2
werkzeug==3.0.1 werkzeug==3.0.1
psutil>=5.9.0,<6.0.0
# nginx - Only needed for Nginx integration, not installed via pip # nginx - Only needed for Nginx integration, not installed via pip
# Run pip install -r requirements.txt # Run pip install -r requirements.txt
+7
View File
@@ -0,0 +1,7 @@
@echo off
timeout /t 2 /nobreak
taskkill /F /PID 15428
set PRODUCTION=true
start "" "python" "app.py"
+17
View File
@@ -0,0 +1,17 @@
#!/bin/bash
sleep 2
# Save current process PID
PID=$1
# Gracefully stop the current server
kill "$PID"
# Wait until it exits
while kill -0 "$PID" 2>/dev/null; do
sleep 0.5
done
# Restart with the same interpreter and script
export PRODUCTION=true
exec "$2" "$3"
+53 -57
View File
@@ -1,7 +1,7 @@
/* ===== Global Reset ===== */ /* ===== Global Reset ===== */
* { * {
box-sizing: border-box; box-sizing: border-box;
margin: 3px; gap: 6px !important;
padding: 0; padding: 0;
} }
@@ -21,21 +21,42 @@ body {
} }
@media (max-width: 600px) { @media (max-width: 600px) {
#sitemap-section,
#password-change-section,
#server-update-section,
#server-status-section,
#server-logs-section,
#system-settings-section {
padding: 20px;
margin-bottom: 20px;
}
#sitemap-section li,
#server-status-section li {
font-size: 0.9em;
padding: 6px;
}
#logContainer {
font-size: 0.9em;
padding: 10px;
}
body { body {
font-size: 11px; font-size: 11px;
padding: 10px; padding: 10px;
} }
.button-group { .button-group,
.admin-button-grid {
flex-direction: column; flex-direction: column;
gap: 12px; align-items: center;
align-items: stretch;
} }
.button-group button { .button-group button,
width: 100%; .admin-button-grid button {
min-width: unset; min-width: 75%;
max-width: 100%; max-width: 75%;
} }
header { header {
@@ -48,7 +69,6 @@ body {
.logo-container { .logo-container {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: 12px;
} }
.logo-container img { .logo-container img {
@@ -77,10 +97,17 @@ body {
.admin-button-grid { .admin-button-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.status-list {
width: 100%;
max-width: 400px;
padding-left: 0;
list-style: none;
word-wrap: break-word;
overflow-wrap: break-word;
}
} }
/* ===== Header ===== */ /* ===== Header ===== */
header { header {
display: flex; display: flex;
@@ -99,7 +126,6 @@ header {
.logo-container { .logo-container {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; /* optional: controls spacing between logo and text */
} }
.logo-container img { .logo-container img {
@@ -135,7 +161,6 @@ main {
width: 100%; width: 100%;
max-width: 800px; max-width: 800px;
padding: 0; padding: 0;
gap: 0;
} }
/* ===== Card Styling ===== */ /* ===== Card Styling ===== */
@@ -153,12 +178,22 @@ main {
display: flex !important; display: flex !important;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: 0px;
max-width: 725px; max-width: 725px;
width: 100%; width: 100%;
} }
.status-list {
width: 100%;
max-width: 400px;
padding-left: 0;
list-style: none;
word-wrap: break-word;
overflow-wrap: break-word;
}
/* ===== Inputs, Textareas, Selects ===== */ /* ===== Inputs, Textareas, Selects ===== */
button, button,
select, select,
input, input,
@@ -182,6 +217,7 @@ input[type="file"] {
color: #28E060; color: #28E060;
text-align: left; text-align: left;
transition: 0.3s; transition: 0.3s;
min-height: 50px;
} }
select { select {
@@ -193,10 +229,6 @@ textarea {
resize: none; resize: none;
} }
input[type="password"] {
min-height: 50px;
}
/* ===== File Input Customization ===== */ /* ===== File Input Customization ===== */
input[type="file"] { input[type="file"] {
border: 2px dashed #28E060; border: 2px dashed #28E060;
@@ -247,7 +279,6 @@ select:focus {
display: flex; display: flex;
flex-wrap: nowrap; flex-wrap: nowrap;
justify-content: center; justify-content: center;
gap: 15px;
width: 100%; width: 100%;
} }
@@ -287,7 +318,6 @@ button {
.admin-button-grid { .admin-button-grid {
display: grid; display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
gap: var(--spacer); /* e.g., 20px between rows and columns */
justify-items: center; justify-items: center;
width: 100%; width: 100%;
max-width: 640px; max-width: 640px;
@@ -308,7 +338,6 @@ button {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
gap: 20px;
} }
.toggle-label { .toggle-label {
@@ -376,7 +405,6 @@ button {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
gap: 12px;
width: 100%; width: 100%;
} }
@@ -524,8 +552,8 @@ footer {
select, select,
#input-text, #input-text,
#output-text { #output-text {
width: 100%; width: 100% !important;
max-width: 90%; max-width: 90% !important;
} }
} }
@@ -556,7 +584,6 @@ footer {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: 8px;
margin-top: 12px; margin-top: 12px;
margin-bottom: 12px; margin-bottom: 12px;
} }
@@ -590,12 +617,6 @@ form {
align-items: center; align-items: center;
} }
form input {
min-height: 50px;
width: 80%;
max-width: 500px;
text-align: left;
}
/* ===== Section Card Styling ===== */ /* ===== Section Card Styling ===== */
section.card { section.card {
@@ -620,7 +641,6 @@ section.card {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: 20px;
margin-bottom: 25px; margin-bottom: 25px;
max-width: 725px; max-width: 725px;
width: 100%; width: 100%;
@@ -774,7 +794,7 @@ section.card {
#sitemap-section li, #sitemap-section li,
#server-status-section li { #server-status-section li {
margin-bottom: 10px; margin-bottom: 6px;
padding: 8px; padding: 8px;
background-color: #2c2f33; background-color: #2c2f33;
border-radius: 6px; border-radius: 6px;
@@ -811,27 +831,3 @@ section.card {
border-radius: 12px; border-radius: 12px;
box-shadow: 0 0 15px #28E060; box-shadow: 0 0 15px #28E060;
} }
/* ===== Mobile Responsive Adjustments ===== */
@media (max-width: 768px) {
#sitemap-section,
#password-change-section,
#server-update-section,
#server-status-section,
#server-logs-section,
#system-settings-section {
padding: 20px;
margin-bottom: 20px;
}
#sitemap-section li,
#server-status-section li {
font-size: 0.9em;
padding: 6px;
}
#logContainer {
font-size: 0.9em;
padding: 10px;
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

+48
View File
@@ -298,6 +298,54 @@ function checkForPacman() {
window.exitGame(); window.exitGame();
} }
} }
function copyShareLink() {
const linkEl = document.getElementById("share-link");
const feedback = document.getElementById("shared-link-feedback");
if (!linkEl) return;
const linkText = linkEl.href || linkEl.textContent.trim();
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(linkText).then(() => {
showCopyFeedback(feedback);
}).catch(() => {
fallbackCopy(linkText, feedback);
});
} else {
fallbackCopy(linkText, feedback);
}
}
function fallbackCopy(text, feedbackEl) {
const tempInput = document.createElement("input");
tempInput.value = text;
document.body.appendChild(tempInput);
tempInput.select();
try {
document.execCommand("copy");
showCopyFeedback(feedbackEl);
} catch (err) {
alert("Copy failed. Please copy manually.");
}
document.body.removeChild(tempInput);
}
function showCopyFeedback(feedbackEl) {
if (!feedbackEl) return;
feedbackEl.style.display = "block";
feedbackEl.classList.add("show");
setTimeout(() => {
feedbackEl.classList.remove("show");
setTimeout(() => {
feedbackEl.style.display = "none";
}, 300);
}, 3000);
}
function startPacman() { } function startPacman() { }
function exitGame() { } function exitGame() { }
+3 -3
View File
@@ -32,14 +32,14 @@
<main> <main>
<section class="card form-group" style="padding: 50px 30px;"> <section class="card form-group" style="padding: 50px 30px;">
<h2 style="color: #ff0066; font-size: 2.5em;">404 - Not Found</h2> <h2 style="color: #ff0066; font-size: 2.5em;">404 - Not Found</h2>
<p class="mt-4" style="font-size: 1.2em; color: #cccccc;"> <p style="font-size: 1.2em; color: #cccccc;">
Whoops! That page doesn't seem to exist. Maybe it got encrypted? Whoops! That page doesn't seem to exist. Maybe it got encrypted?
</p> </p>
<!-- Navigation --> <!-- Navigation -->
<div class="button-group mt-4"> <div class="button-group">
<a href="{{ url_for('index') }}"> <a href="{{ url_for('index') }}">
<button type="button">⬅️ Return Home</button> <button type="button">Return Home</button>
</a> </a>
</div> </div>
</section> </section>
+3 -3
View File
@@ -31,16 +31,16 @@
<!-- Main Content --> <!-- Main Content -->
<main> <main>
<section class="card form-group" style="padding: 50px 30px;"> <section class="card form-group" style="padding: 50px 30px;">
<h2 style="color: #ff3300; font-size: 2.5em;">💥 500 - Server Error</h2> <h2 style="color: #ff3300; font-size: 2.5em;">500 - Server Error</h2>
<p class="mt-4" style="font-size: 1.2em; color: #cccccc;"> <p class="mt-4" style="font-size: 1.2em; color: #cccccc;">
Uh oh! The ghosts chomped the server wires. 🧟‍♂️👾 Uh oh! The ghosts chomped the server wires.
We're working on patching it up. We're working on patching it up.
</p> </p>
<!-- Navigation --> <!-- Navigation -->
<div class="button-group mt-4"> <div class="button-group mt-4">
<a href="{{ url_for('index') }}"> <a href="{{ url_for('index') }}">
<button type="button">⬅️ Return Home</button> <button type="button">Return Home</button>
</a> </a>
</div> </div>
</section> </section>
+9 -8
View File
@@ -53,12 +53,12 @@
<form action="{{ url_for('admin_logout') }}" method="GET" style="display: inline;"> <form action="{{ url_for('admin_logout') }}" method="GET" style="display: inline;">
<button type="submit">Log Out</button> <button type="submit">Log Out</button>
</form> </form>
<button onclick="updateServer()">Pull Latest Changes</button> <button onclick="updateServer()">Update Server</button>
<form action="{{ url_for('admin_settings') }}" method="GET" style="display: inline;"> <form action="{{ url_for('admin_settings') }}" method="GET" style="display: inline;">
<button type="submit">Manage Upload Settings</button> <button type="submit">Settings</button>
</form> </form>
<button onclick="resetAdmin()" class="danger-button">Reset Admin</button> <button onclick="resetAdmin()" class="danger-button">Reset Admin</button>
<button onclick="clearUploads()" class="danger-button">Clear Uploaded Files</button> <button onclick="clearUploads()" class="danger-button">Clear PacShare</button>
</div> </div>
@@ -88,14 +88,15 @@
<!-- Server Status Section --> <!-- Server Status Section -->
<section id="server-status-section" class="card form-group"> <section id="server-status-section" class="card form-group">
<h2>Server Status</h2> <h2>Server Status</h2>
<ul style="width: 400px;"> <ul class="status-list">
<li>Uptime: <code>0 days, 11 hours, 47 minutes</code></li> <li>Uptime: <code>{{ server_info.uptime }}</code></li>
<li>Server Time: <code>2025-05-14 14:32:18</code></li> <li>Server Time: <code>{{ server_info.server_time }}</code></li>
<li>Python Version: <code>3.13.3</code></li> <li>Python Version: <code>{{ server_info.python_version }}</code></li>
<li>Flask Debug Mode: <code>True</code></li> <li>Flask Debug Mode: <code>{{ server_info.debug_mode }}</code></li>
</ul> </ul>
</section> </section>
<!-- Server Logs Section --> <!-- Server Logs Section -->
<section id="server-logs-section" class="card form-group"> <section id="server-logs-section" class="card form-group">
<h2>Server Logs</h2> <h2>Server Logs</h2>
+11 -7
View File
@@ -18,16 +18,20 @@
</head> </head>
<body class="dark"> <body class="dark">
<!-- Header --> <!-- Header -->
<header class="card"> <header class="card logo-header">
<h1>PacCrypt Admin</h1> <div class="logo-container">
<p>Administrator Login</p> <img src="{{ url_for('static', filename='img/PacCrypt.png') }}" alt="PacCrypt Logo" />
</header> <div class="logo-text">
<h1>PACCRYPT</h1>
<p>Admin Login</p>
</div>
</div>
</header>
<!-- Main Content --> <!-- Main Content -->
<main> <main>
<!-- Login Form Section --> <!-- Login Form Section -->
<section class="card form-group"> <section class="card form-group">
<h2>🔑 Admin Login</h2> <h2>Admin Login</h2>
<!-- Flash Messages --> <!-- Flash Messages -->
{% with messages = get_flashed_messages() %} {% with messages = get_flashed_messages() %}
@@ -41,7 +45,7 @@
<input type="text" name="username" placeholder="Username" required /> <input type="text" name="username" placeholder="Username" required />
<input type="password" name="password" placeholder="Password" required /> <input type="password" name="password" placeholder="Password" required />
<div class="button-group mt-3"> <div class="button-group mt-3">
<button type="submit">🚪 Log In</button> <button type="submit">Log In</button>
</div> </div>
</form> </form>
</section> </section>
+13 -8
View File
@@ -16,16 +16,21 @@
</head> </head>
<body class="dark"> <body class="dark">
<!-- Header --> <!-- Header -->
<header class="card"> <!-- Header -->
<h1>PacCrypt Admin Settings</h1> <header class="card logo-header">
<p>Manage upload configuration</p> <div class="logo-container">
</header> <img src="{{ url_for('static', filename='img/PacCrypt.png') }}" alt="PacCrypt Logo" />
<div class="logo-text">
<h1>PACCRYPT</h1>
<p>Server Settings</p>
</div>
</div>
</header>
<!-- Main Content --> <!-- Main Content -->
<main> <main>
<!-- Settings Form Section --> <!-- Settings Form Section -->
<section class="card form-group"> <section class="card form-group">
<h2>⚙️ Upload Settings</h2> <h2>Upload Settings</h2>
<!-- Flash Messages --> <!-- Flash Messages -->
{% with messages = get_flashed_messages() %} {% with messages = get_flashed_messages() %}
@@ -51,9 +56,9 @@
<!-- Action Buttons --> <!-- Action Buttons -->
<div class="button-group mt-4"> <div class="button-group mt-4">
<button type="submit">💾 Save Settings</button> <button type="submit">Save Settings</button>
<a href="{{ url_for('admin_page') }}"> <a href="{{ url_for('admin_page') }}">
<button type="button">⬅️ Back to Admin Panel</button> <button type="button">Back to Admin Panel</button>
</a> </a>
</div> </div>
</form> </form>
+6 -7
View File
@@ -49,7 +49,7 @@
<canvas id="pacmanCanvas" width="800" height="600"></canvas> <canvas id="pacmanCanvas" width="800" height="600"></canvas>
</div> </div>
<audio id="chomp-sound" src="{{ url_for('static', filename='audio/chomp.mp3') }}"></audio> <audio id="chomp-sound" src="{{ url_for('static', filename='audio/chomp.mp3') }}"></audio>
<div class="button-group"> <div class="button-group" style="margin-top: 6px;">
<button type="button" onclick="resetGame()">Restart Game</button> <button type="button" onclick="resetGame()">Restart Game</button>
<button type="button" onclick="exitGame()">Exit Game</button> <button type="button" onclick="exitGame()">Exit Game</button>
</div> </div>
@@ -112,9 +112,8 @@
<!-- File Sharing Section --> <!-- File Sharing Section -->
<section id="sharing-section" class="card form-group"> <section id="sharing-section" class="card form-group">
<h2>PacCrypt Share</h2> <h2 style="margin-bottom: unset;">PacShare</h2>
<h3>Securely share encrypted files.</h3> <p style="margin-top: unset;">Securely share encrypted files.</p>
<p>Do not lose your passwords, data will be lost forever!</p>
<!-- Flash Messages --> <!-- Flash Messages -->
{% with messages = get_flashed_messages() %} {% with messages = get_flashed_messages() %}
@@ -126,8 +125,7 @@
{% if "pickup" in message %} {% if "pickup" in message %}
<div class="share-link-container"> <div class="share-link-container">
<a id="share-link" href="{{ message.split(' at ')[1] }}" target="_blank">{{ message.split(" at ")[1] }}</a> <a id="share-link" href="{{ message.split(' at ')[1] }}" target="_blank">{{ message.split(" at ")[1] }}</a>
<!--- <span id="share-link">{{ message.split(" at ")[1] }}</span> ---> <button type="button" onclick="copyShareLink()">Copy Link</button>
<button type="button" id="copy-share-btn">Copy Link</button>
<div id="shared-link-feedback" class="copy-feedback">Link copied to clipboard!</div> <div id="shared-link-feedback" class="copy-feedback">Link copied to clipboard!</div>
</div> </div>
{% endif %} {% endif %}
@@ -138,6 +136,7 @@
{% endif %} {% endif %}
{% endwith %} {% endwith %}
<!-- File Upload Form --> <!-- File Upload Form -->
<!-- Share Link Container (initially hidden) --> <!-- Share Link Container (initially hidden) -->
<div class="share-link-container" id="share-link-container" style="display: none;"> <div class="share-link-container" id="share-link-container" style="display: none;">
@@ -153,7 +152,7 @@
<button type="submit">Upload and Generate Link</button> <button type="submit">Upload and Generate Link</button>
</div> </div>
</form> </form>
<p style="color: #9c0000;">BOTH PASSWORDS ARE REQUIRED FOR PICKUP</p>
<script> <script>
document.getElementById('upload-form').addEventListener('submit', async (e) => { document.getElementById('upload-form').addEventListener('submit', async (e) => {
e.preventDefault(); e.preventDefault();
+12 -7
View File
@@ -15,16 +15,21 @@
</head> </head>
<body class="dark"> <body class="dark">
<!-- Header --> <!-- Header -->
<header class="card"> <header class="card logo-header">
<h1>PacCrypt</h1> <div class="logo-container">
<p>Secure File Pickup and Decryption</p> <img src="{{ url_for('static', filename='img/PacCrypt.png') }}" alt="PacCrypt Logo" />
</header> <div class="logo-text">
<h1>PACCRYPT</h1>
<p>Encrypted File Pickup</p>
</div>
</div>
</header>
<!-- Main Content --> <!-- Main Content -->
<main> <main>
<!-- File Pickup Section --> <!-- File Pickup Section -->
<section id="pickup-section" class="card form-group"> <section id="pickup-section" class="card form-group">
<h2>🔐 File Pickup</h2> <h2>File Pickup</h2>
<!-- Flash Messages --> <!-- Flash Messages -->
{% with messages = get_flashed_messages() %} {% with messages = get_flashed_messages() %}
@@ -61,14 +66,14 @@
</div> </div>
<div class="button-group"> <div class="button-group">
<button type="submit">📥 Decrypt and Download</button> <button type="submit">Decrypt and Download</button>
</div> </div>
</form> </form>
</section> </section>
<!-- Security Notice Section --> <!-- Security Notice Section -->
<section id="security-notice-section" class="card form-group"> <section id="security-notice-section" class="card form-group">
<h2>🛡️ Security Notice</h2> <h2>Security Notice</h2>
<p style="color: #00ff99; text-align: center;"> <p style="color: #00ff99; text-align: center;">
Make sure you're on the correct domain before entering any passwords.<br> Make sure you're on the correct domain before entering any passwords.<br>
Your file will be permanently deleted after download. Your file will be permanently deleted after download.