Better UI for both Desktop and Mobile
This commit is contained in:
@@ -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 safer and cleaner restart script
|
||||||
# Create a shell script to restart the server
|
restart_script = """#!/bin/bash
|
||||||
restart_script = f"""#!/bin/bash
|
sleep 2
|
||||||
sleep 2
|
PID=$1
|
||||||
kill -9 {current_pid}
|
kill "$PID"
|
||||||
export PRODUCTION=true
|
while kill -0 "$PID" 2>/dev/null; do sleep 0.5; done
|
||||||
{python_path} {script_path}
|
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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
@echo off
|
||||||
|
timeout /t 2 /nobreak
|
||||||
|
taskkill /F /PID 15428
|
||||||
|
set PRODUCTION=true
|
||||||
|
start "" "python" "app.py"
|
||||||
|
|
||||||
+17
@@ -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"
|
||||||
+54
-58
@@ -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,9 +97,16 @@ 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 {
|
||||||
@@ -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;
|
||||||
@@ -810,28 +830,4 @@ section.card {
|
|||||||
background-color: #1e1e1e;
|
background-color: #1e1e1e;
|
||||||
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 |
+353
-305
@@ -1,305 +1,353 @@
|
|||||||
/**
|
/**
|
||||||
* UI management module.
|
* UI management module.
|
||||||
* Handles user interface interactions and form handling.
|
* Handles user interface interactions and form handling.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { encryptFile, decryptFile } from './fileops.js';
|
import { encryptFile, decryptFile } from './fileops.js';
|
||||||
|
|
||||||
// ===== UI Initialization =====
|
// ===== UI Initialization =====
|
||||||
export function setupUI() {
|
export function setupUI() {
|
||||||
// Set initial state of remove button to hidden
|
// Set initial state of remove button to hidden
|
||||||
const removeBtn = document.getElementById("remove-file-btn");
|
const removeBtn = document.getElementById("remove-file-btn");
|
||||||
if (removeBtn) {
|
if (removeBtn) {
|
||||||
removeBtn.style.display = "none";
|
removeBtn.style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeEventListeners();
|
initializeEventListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Event Listeners =====
|
// ===== Event Listeners =====
|
||||||
function initializeEventListeners() {
|
function initializeEventListeners() {
|
||||||
const elements = {
|
const elements = {
|
||||||
encryptionType: document.getElementById("encryption-type"),
|
encryptionType: document.getElementById("encryption-type"),
|
||||||
inputText: document.getElementById("input-text"),
|
inputText: document.getElementById("input-text"),
|
||||||
form: document.getElementById("crypto-form"),
|
form: document.getElementById("crypto-form"),
|
||||||
removeFileBtn: document.getElementById("remove-file-btn"),
|
removeFileBtn: document.getElementById("remove-file-btn"),
|
||||||
clearAllBtn: document.getElementById("clear-all-btn"),
|
clearAllBtn: document.getElementById("clear-all-btn"),
|
||||||
generateBtn: document.getElementById("generate-btn"),
|
generateBtn: document.getElementById("generate-btn"),
|
||||||
copyPasswordBtn: document.getElementById("copy-btn"),
|
copyPasswordBtn: document.getElementById("copy-btn"),
|
||||||
copyOutputBtn: document.getElementById("copy-output-btn"),
|
copyOutputBtn: document.getElementById("copy-output-btn"),
|
||||||
toggleSwitch: document.getElementById("operation-toggle"),
|
toggleSwitch: document.getElementById("operation-toggle"),
|
||||||
copyShareBtn: document.getElementById("copy-share-btn"),
|
copyShareBtn: document.getElementById("copy-share-btn"),
|
||||||
shareLink: document.getElementById("share-link")
|
shareLink: document.getElementById("share-link")
|
||||||
};
|
};
|
||||||
|
|
||||||
if (validateElements(elements)) {
|
if (validateElements(elements)) {
|
||||||
setupElementListeners(elements);
|
setupElementListeners(elements);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateElements(elements) {
|
function validateElements(elements) {
|
||||||
return elements.encryptionType && elements.inputText && elements.form &&
|
return elements.encryptionType && elements.inputText && elements.form &&
|
||||||
elements.removeFileBtn && elements.clearAllBtn && elements.generateBtn &&
|
elements.removeFileBtn && elements.clearAllBtn && elements.generateBtn &&
|
||||||
elements.copyPasswordBtn && elements.toggleSwitch;
|
elements.copyPasswordBtn && elements.toggleSwitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupElementListeners(elements) {
|
function setupElementListeners(elements) {
|
||||||
elements.encryptionType.addEventListener("change", toggleEncryptionOptions);
|
elements.encryptionType.addEventListener("change", toggleEncryptionOptions);
|
||||||
elements.inputText.addEventListener("input", handleInputChange);
|
elements.inputText.addEventListener("input", handleInputChange);
|
||||||
elements.form.addEventListener("submit", handleSubmit);
|
elements.form.addEventListener("submit", handleSubmit);
|
||||||
elements.removeFileBtn.addEventListener("click", removeFile);
|
elements.removeFileBtn.addEventListener("click", removeFile);
|
||||||
elements.clearAllBtn.addEventListener("click", clearAll);
|
elements.clearAllBtn.addEventListener("click", clearAll);
|
||||||
elements.generateBtn.addEventListener("click", generateRandomPassword);
|
elements.generateBtn.addEventListener("click", generateRandomPassword);
|
||||||
elements.copyPasswordBtn.addEventListener("click", () => copyToClipboard("generated-password", "password-copy-feedback"));
|
elements.copyPasswordBtn.addEventListener("click", () => copyToClipboard("generated-password", "password-copy-feedback"));
|
||||||
elements.copyOutputBtn?.addEventListener("click", () => copyToClipboard("output-text", "output-copy-feedback"));
|
elements.copyOutputBtn?.addEventListener("click", () => copyToClipboard("output-text", "output-copy-feedback"));
|
||||||
elements.toggleSwitch.addEventListener("change", () => {
|
elements.toggleSwitch.addEventListener("change", () => {
|
||||||
console.log("Mode:", elements.toggleSwitch.checked ? "Decrypt" : "Encrypt");
|
console.log("Mode:", elements.toggleSwitch.checked ? "Decrypt" : "Encrypt");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Add file input change listener
|
// Add file input change listener
|
||||||
const fileInput = document.getElementById("file-input");
|
const fileInput = document.getElementById("file-input");
|
||||||
if (fileInput) {
|
if (fileInput) {
|
||||||
fileInput.addEventListener("change", () => {
|
fileInput.addEventListener("change", () => {
|
||||||
const removeBtn = document.getElementById("remove-file-btn");
|
const removeBtn = document.getElementById("remove-file-btn");
|
||||||
if (removeBtn) {
|
if (removeBtn) {
|
||||||
removeBtn.style.display = fileInput.files.length > 0 ? "inline-block" : "none";
|
removeBtn.style.display = fileInput.files.length > 0 ? "inline-block" : "none";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setupShareLinkListeners(elements);
|
setupShareLinkListeners(elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupShareLinkListeners(elements) {
|
function setupShareLinkListeners(elements) {
|
||||||
if (elements.copyShareBtn && elements.shareLink) {
|
if (elements.copyShareBtn && elements.shareLink) {
|
||||||
elements.copyShareBtn.addEventListener("click", () => {
|
elements.copyShareBtn.addEventListener("click", () => {
|
||||||
const linkText = elements.shareLink.textContent.trim();
|
const linkText = elements.shareLink.textContent.trim();
|
||||||
navigator.clipboard.writeText(linkText).then(() => {
|
navigator.clipboard.writeText(linkText).then(() => {
|
||||||
const feedback = document.getElementById("shared-link-feedback");
|
const feedback = document.getElementById("shared-link-feedback");
|
||||||
if (feedback) {
|
if (feedback) {
|
||||||
feedback.style.display = "block";
|
feedback.style.display = "block";
|
||||||
feedback.classList.add("show");
|
feedback.classList.add("show");
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
feedback.classList.remove("show");
|
feedback.classList.remove("show");
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
feedback.style.display = "none";
|
feedback.style.display = "none";
|
||||||
}, 300);
|
}, 300);
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== UI State Management =====
|
// ===== UI State Management =====
|
||||||
function toggleEncryptionOptions() {
|
function toggleEncryptionOptions() {
|
||||||
const type = document.getElementById("encryption-type").value.trim().toLowerCase();
|
const type = document.getElementById("encryption-type").value.trim().toLowerCase();
|
||||||
const passwordInputWrapper = document.getElementById("password-input");
|
const passwordInputWrapper = document.getElementById("password-input");
|
||||||
const fileSection = document.querySelector("#encoding-section #file-section");
|
const fileSection = document.querySelector("#encoding-section #file-section");
|
||||||
const isAdvanced = type.includes("advanced");
|
const isAdvanced = type.includes("advanced");
|
||||||
|
|
||||||
if (passwordInputWrapper) {
|
if (passwordInputWrapper) {
|
||||||
if (isAdvanced) {
|
if (isAdvanced) {
|
||||||
passwordInputWrapper.classList.remove("hidden");
|
passwordInputWrapper.classList.remove("hidden");
|
||||||
} else {
|
} else {
|
||||||
passwordInputWrapper.classList.add("hidden");
|
passwordInputWrapper.classList.add("hidden");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileSection) {
|
if (fileSection) {
|
||||||
if (isAdvanced) {
|
if (isAdvanced) {
|
||||||
fileSection.classList.remove("hidden");
|
fileSection.classList.remove("hidden");
|
||||||
} else {
|
} else {
|
||||||
fileSection.classList.add("hidden");
|
fileSection.classList.add("hidden");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateToggleLabels();
|
updateToggleLabels();
|
||||||
toggleInputMode();
|
toggleInputMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateToggleLabels() {
|
function updateToggleLabels() {
|
||||||
const type = document.getElementById("encryption-type")?.value;
|
const type = document.getElementById("encryption-type")?.value;
|
||||||
const leftLabel = document.getElementById("toggle-left-label");
|
const leftLabel = document.getElementById("toggle-left-label");
|
||||||
const rightLabel = document.getElementById("toggle-right-label");
|
const rightLabel = document.getElementById("toggle-right-label");
|
||||||
|
|
||||||
if (!type || !leftLabel || !rightLabel) return;
|
if (!type || !leftLabel || !rightLabel) return;
|
||||||
|
|
||||||
const isAdvanced = type.toLowerCase().includes("advanced");
|
const isAdvanced = type.toLowerCase().includes("advanced");
|
||||||
leftLabel.textContent = isAdvanced ? "Encrypt" : "Encode";
|
leftLabel.textContent = isAdvanced ? "Encrypt" : "Encode";
|
||||||
rightLabel.textContent = isAdvanced ? "Decrypt" : "Decode";
|
rightLabel.textContent = isAdvanced ? "Decrypt" : "Decode";
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleInputMode() {
|
function toggleInputMode() {
|
||||||
const fileInput = document.getElementById("file-input");
|
const fileInput = document.getElementById("file-input");
|
||||||
const textValue = document.getElementById("input-text")?.value.trim();
|
const textValue = document.getElementById("input-text")?.value.trim();
|
||||||
const isAdvanced = document.getElementById("encryption-type")?.value === "advanced";
|
const isAdvanced = document.getElementById("encryption-type")?.value === "advanced";
|
||||||
|
|
||||||
const textSection = document.getElementById("text-section");
|
const textSection = document.getElementById("text-section");
|
||||||
const fileSection = document.getElementById("file-section");
|
const fileSection = document.getElementById("file-section");
|
||||||
const removeBtn = document.getElementById("remove-file-btn");
|
const removeBtn = document.getElementById("remove-file-btn");
|
||||||
|
|
||||||
if (!fileInput || !textSection || !fileSection || !removeBtn) return;
|
if (!fileInput || !textSection || !fileSection || !removeBtn) return;
|
||||||
|
|
||||||
const fileSelected = fileInput.files.length > 0;
|
const fileSelected = fileInput.files.length > 0;
|
||||||
|
|
||||||
textSection.style.display = fileSelected ? "none" : "flex";
|
textSection.style.display = fileSelected ? "none" : "flex";
|
||||||
fileSection.style.display = (isAdvanced && !textValue) ? "flex" : "none";
|
fileSection.style.display = (isAdvanced && !textValue) ? "flex" : "none";
|
||||||
removeBtn.style.display = fileSelected ? "inline-block" : "none";
|
removeBtn.style.display = fileSelected ? "inline-block" : "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Form Handling =====
|
// ===== Form Handling =====
|
||||||
async function handleSubmit(event) {
|
async function handleSubmit(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const encryptionType = document.getElementById("encryption-type")?.value;
|
const encryptionType = document.getElementById("encryption-type")?.value;
|
||||||
const password = document.getElementById("password")?.value;
|
const password = document.getElementById("password")?.value;
|
||||||
const fileInput = document.getElementById("file-input");
|
const fileInput = document.getElementById("file-input");
|
||||||
const isDecrypt = document.getElementById("operation-toggle").checked;
|
const isDecrypt = document.getElementById("operation-toggle").checked;
|
||||||
const operation = isDecrypt ? "decrypt" : "encrypt";
|
const operation = isDecrypt ? "decrypt" : "encrypt";
|
||||||
|
|
||||||
if (!encryptionType || !fileInput) return;
|
if (!encryptionType || !fileInput) return;
|
||||||
|
|
||||||
if (encryptionType === "advanced" && !password) {
|
if (encryptionType === "advanced" && !password) {
|
||||||
return alert("Password is required for advanced encryption.");
|
return alert("Password is required for advanced encryption.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileInput.files.length > 0) {
|
if (fileInput.files.length > 0) {
|
||||||
return (operation === "encrypt")
|
return (operation === "encrypt")
|
||||||
? encryptFile(fileInput, password)
|
? encryptFile(fileInput, password)
|
||||||
: decryptFile(fileInput, password);
|
: decryptFile(fileInput, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
await handleTextOperation(encryptionType, operation, password);
|
await handleTextOperation(encryptionType, operation, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleTextOperation(encryptionType, operation, password) {
|
async function handleTextOperation(encryptionType, operation, password) {
|
||||||
const payload = {
|
const payload = {
|
||||||
"encryption-type": encryptionType,
|
"encryption-type": encryptionType,
|
||||||
operation: operation,
|
operation: operation,
|
||||||
message: document.getElementById("input-text")?.value,
|
message: document.getElementById("input-text")?.value,
|
||||||
password: password
|
password: password
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch("/", {
|
const response = await fetch("/", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify(payload)
|
body: JSON.stringify(payload)
|
||||||
});
|
});
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
document.getElementById("output-text").value = data.result;
|
document.getElementById("output-text").value = data.result;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert("Error processing request: " + err.message);
|
alert("Error processing request: " + err.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Utility Functions =====
|
// ===== Utility Functions =====
|
||||||
function removeFile() {
|
function removeFile() {
|
||||||
const fileInput = document.getElementById("file-input");
|
const fileInput = document.getElementById("file-input");
|
||||||
if (fileInput) fileInput.value = "";
|
if (fileInput) fileInput.value = "";
|
||||||
const removeBtn = document.getElementById("remove-file-btn");
|
const removeBtn = document.getElementById("remove-file-btn");
|
||||||
if (removeBtn) removeBtn.style.display = 'none';
|
if (removeBtn) removeBtn.style.display = 'none';
|
||||||
toggleInputMode();
|
toggleInputMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateRandomPassword() {
|
function generateRandomPassword() {
|
||||||
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+[]{}|;:,.<>?/~";
|
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+[]{}|;:,.<>?/~";
|
||||||
const length = 30;
|
const length = 30;
|
||||||
const password = Array.from({ length }, () =>
|
const password = Array.from({ length }, () =>
|
||||||
charset.charAt(Math.floor(Math.random() * charset.length))
|
charset.charAt(Math.floor(Math.random() * charset.length))
|
||||||
).join("");
|
).join("");
|
||||||
const passwordField = document.getElementById("generated-password");
|
const passwordField = document.getElementById("generated-password");
|
||||||
if (passwordField) {
|
if (passwordField) {
|
||||||
passwordField.value = password;
|
passwordField.value = password;
|
||||||
// Check if we should start Pacman
|
// Check if we should start Pacman
|
||||||
checkForPacman();
|
checkForPacman();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyToClipboard(elementId, feedbackId) {
|
function copyToClipboard(elementId, feedbackId) {
|
||||||
const el = document.getElementById(elementId);
|
const el = document.getElementById(elementId);
|
||||||
const feedback = document.getElementById(feedbackId);
|
const feedback = document.getElementById(feedbackId);
|
||||||
|
|
||||||
if (!el || !el.value) return;
|
if (!el || !el.value) return;
|
||||||
|
|
||||||
// Create a temporary textarea element
|
// Create a temporary textarea element
|
||||||
const textarea = document.createElement('textarea');
|
const textarea = document.createElement('textarea');
|
||||||
textarea.value = el.value;
|
textarea.value = el.value;
|
||||||
textarea.style.position = 'fixed';
|
textarea.style.position = 'fixed';
|
||||||
textarea.style.opacity = '0';
|
textarea.style.opacity = '0';
|
||||||
document.body.appendChild(textarea);
|
document.body.appendChild(textarea);
|
||||||
|
|
||||||
// Select and copy the text
|
// Select and copy the text
|
||||||
textarea.select();
|
textarea.select();
|
||||||
textarea.setSelectionRange(0, 99999); // For mobile devices
|
textarea.setSelectionRange(0, 99999); // For mobile devices
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Try using the modern clipboard API first
|
// Try using the modern clipboard API first
|
||||||
navigator.clipboard.writeText(el.value).then(() => {
|
navigator.clipboard.writeText(el.value).then(() => {
|
||||||
showFeedback(feedback);
|
showFeedback(feedback);
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
// Fallback to execCommand for older browsers
|
// Fallback to execCommand for older browsers
|
||||||
document.execCommand('copy');
|
document.execCommand('copy');
|
||||||
showFeedback(feedback);
|
showFeedback(feedback);
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Final fallback
|
// Final fallback
|
||||||
document.execCommand('copy');
|
document.execCommand('copy');
|
||||||
showFeedback(feedback);
|
showFeedback(feedback);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
document.body.removeChild(textarea);
|
document.body.removeChild(textarea);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showFeedback(feedback) {
|
function showFeedback(feedback) {
|
||||||
if (feedback) {
|
if (feedback) {
|
||||||
feedback.style.display = "block";
|
feedback.style.display = "block";
|
||||||
feedback.classList.add("show");
|
feedback.classList.add("show");
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
feedback.classList.remove("show");
|
feedback.classList.remove("show");
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
feedback.style.display = "none";
|
feedback.style.display = "none";
|
||||||
}, 300);
|
}, 300);
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearAll() {
|
function clearAll() {
|
||||||
const fields = ["input-text", "output-text", "file-input", "password"];
|
const fields = ["input-text", "output-text", "file-input", "password"];
|
||||||
fields.forEach(id => {
|
fields.forEach(id => {
|
||||||
const el = document.getElementById(id);
|
const el = document.getElementById(id);
|
||||||
if (el) el.value = "";
|
if (el) el.value = "";
|
||||||
});
|
});
|
||||||
removeFile();
|
removeFile();
|
||||||
toggleInputMode();
|
toggleInputMode();
|
||||||
document.getElementById("pacman-section")?.style.setProperty("display", "none");
|
document.getElementById("pacman-section")?.style.setProperty("display", "none");
|
||||||
document.getElementById("encoding-section")?.style.setProperty("display", "block");
|
document.getElementById("encoding-section")?.style.setProperty("display", "block");
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleInputChange() {
|
function handleInputChange() {
|
||||||
toggleInputMode();
|
toggleInputMode();
|
||||||
checkForPacman();
|
checkForPacman();
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkForPacman() {
|
function checkForPacman() {
|
||||||
const val = document.getElementById("input-text").value.trim().toLowerCase();
|
const val = document.getElementById("input-text").value.trim().toLowerCase();
|
||||||
const pacSection = document.getElementById("pacman-section");
|
const pacSection = document.getElementById("pacman-section");
|
||||||
const encSection = document.getElementById("encoding-section");
|
const encSection = document.getElementById("encoding-section");
|
||||||
|
|
||||||
if (val.includes("pacman") && pacSection.style.display !== "block") {
|
if (val.includes("pacman") && pacSection.style.display !== "block") {
|
||||||
pacSection.style.display = "block";
|
pacSection.style.display = "block";
|
||||||
encSection.style.display = "none";
|
encSection.style.display = "none";
|
||||||
window.startPacman();
|
window.startPacman();
|
||||||
} else if (pacSection.style.display === "block" && !val.includes("pacman")) {
|
} else if (pacSection.style.display === "block" && !val.includes("pacman")) {
|
||||||
window.exitGame();
|
window.exitGame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function copyShareLink() {
|
||||||
function startPacman() { }
|
const linkEl = document.getElementById("share-link");
|
||||||
function exitGame() { }
|
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 exitGame() { }
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -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
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user