This commit is contained in:
justuser-31 2026-02-03 19:27:57 +03:00
parent 4409a844c0
commit 402974eaf6
3 changed files with 450 additions and 111 deletions

View File

@ -58,19 +58,13 @@ class FileServerHandler(BaseHTTPRequestHandler):
def handle_main_page(self): def handle_main_page(self):
content = f""" content = f"""
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>SimpliestFS</title> <title>SimpliestFS</title>
<style> <link rel="stylesheet" href="/static/style.css">
body {{ font-family: sans-serif; margin: 40px; font-size: 1em; }} </head>
.btn {{ font-size: 1.5em; display: inline-block; padding: 10px 20px; margin: 10px; background: #eee; border: 1px solid #ccc; text-decoration: none; color: #333; }} <body class="main-page">
.btn:hover {{ background: #ddd; }}
.disclaimer {{ margin-top: 30px; font-size: 0.9em; color: #666; }}
h1 a {{ text-decoration: none; color: inherit; }}
</style>
</head>
<body>
<h1><a href="/">SimpliestFS</a></h1> <h1><a href="/">SimpliestFS</a></h1>
<p>EN: {CONFIG["ui"]["disclaimer"]}</p> <p>EN: {CONFIG["ui"]["disclaimer"]}</p>
<p>RU: {CONFIG["ui"]["disclaimer_ru"]}</p> <p>RU: {CONFIG["ui"]["disclaimer_ru"]}</p>
@ -84,30 +78,26 @@ class FileServerHandler(BaseHTTPRequestHandler):
<div class="disclaimer"> <div class="disclaimer">
Note: NO ENCRYPTION, no external databases. As simple as possible. Note: NO ENCRYPTION, no external databases. As simple as possible.
</div> </div>
</body> </body>
</html> </html>
""" """
self.send_html(content) self.send_html(content)
def handle_register_page(self): def handle_register_page(self):
content = f""" content = f"""
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>Register - SimpliestFS</title> <title>Register - SimpliestFS</title>
<style> <link rel="stylesheet" href="/static/style.css">
body {{ font-family: sans-serif; margin: 40px; font-size: 2em; }} </head>
.back-btn {{ display: inline-block; padding: 5px 10px; background: #eee; border: 1px solid #ccc; text-decoration: none; color: #333; }} <body>
h1 a {{ text-decoration: none; color: inherit; }}
</style>
</head>
<body>
<h1><a href="/">SimpliestFS</a></h1> <h1><a href="/">SimpliestFS</a></h1>
<h2>Register</h2> <h2>Register</h2>
<p>{CONFIG["ui"]["register_info"]}</p> <p>{CONFIG["ui"]["register_info"]}</p>
<p><a class="back-btn" href="/">Back to Home</a></p> <p><a class="back-btn" href="/">Back to Home</a></p>
</body> </body>
</html> </html>
""" """
self.send_html(content) self.send_html(content)
@ -161,31 +151,24 @@ class FileServerHandler(BaseHTTPRequestHandler):
def render_login_form(self, error=""): def render_login_form(self, error=""):
content = f""" content = f"""
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>Login - SimpliestFS</title> <title>Login - SimpliestFS</title>
<style> <link rel="stylesheet" href="/static/style.css">
body {{ font-family: sans-serif; margin: 40px; font-size: 1em; }} </head>
form {{ max-width: 300px; }} <body>
input, button {{ width: 100%; padding: 8px; margin: 5px 0; font-size: 2vh; }}
.error {{ color: red; }}
.back-btn {{ display: inline-block; padding: 5px 10px; background: #eee; border: 1px solid #ccc; text-decoration: none; color: #333; }}
h1 a {{ text-decoration: none; color: inherit; }}
</style>
</head>
<body>
<h1><a href="/">SimpliestFS</a></h1> <h1><a href="/">SimpliestFS</a></h1>
<h2>Login</h2> <h2>Login</h2>
{f'<p class="error">{error}</p>' if error else ""} {f'<p class="error">{error}</p>' if error else ""}
<form method="POST"> <form method="POST" class="login-form">
<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>
<button type="submit">Login</button> <button type="submit">Login</button>
</form> </form>
<p><a class="back-btn" href="/">Back to Home</a></p> <p><a class="back-btn" href="/">Back to Home</a></p>
</body> </body>
</html> </html>
""" """
return content return content
@ -227,28 +210,21 @@ class FileServerHandler(BaseHTTPRequestHandler):
""" """
content = f""" content = f"""
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>My Files - SimpliestFS</title> <title>My Files - SimpliestFS</title>
<style> <link rel="stylesheet" href="/static/style.css">
body {{ font-family: sans-serif; margin: 40px; font-size: 1.5em; }} </head>
.upload-btn {{ display: inline-block; padding: 5px 10px; background: #eee; border: 1px solid #ccc; text-decoration: none; color: #333; }} <body>
.back-btn {{ display: inline-block; padding: 5px 10px; background: #eee; border: 1px solid #ccc; text-decoration: none; color: #333; }}
.quota-info {{ margin: 20px 0; font-size: 0.9em; color: #666; }}
.regular-button {{ font-size: 1em; }}
h1 a {{ text-decoration: none; color: inherit; }}
</style>
</head>
<body>
<h1><a href="/">SimpliestFS</a></h1> <h1><a href="/">SimpliestFS</a></h1>
<h2>My Files</h2> <h2>My Files</h2>
<div class="quota-info"> <div class="quota-info">
Quota: {available_mb} MB | Used: {used_mb:.2f} MB Quota: {available_mb} MB | Used: {used_mb:.2f} MB
</div> </div>
<form method="POST" enctype="multipart/form-data" action="/upload"> <form method="POST" enctype="multipart/form-data" action="/upload">
<input class = "regular-button" type="file" name="file" required> <input class="regular-button" type="file" name="file" required>
<button class = "regular-button" type="submit">Upload</button> <button class="regular-button" type="submit">Upload</button>
</form> </form>
<hr> <hr>
{files_html if files_html else "<p>No files yet.</p>"} {files_html if files_html else "<p>No files yet.</p>"}
@ -256,8 +232,8 @@ class FileServerHandler(BaseHTTPRequestHandler):
<a class="back-btn" href="/logout">Logout</a> <a class="back-btn" href="/logout">Logout</a>
<a class="back-btn" href="/">Back to Home</a> <a class="back-btn" href="/">Back to Home</a>
</p> </p>
</body> </body>
</html> </html>
""" """
self.send_html(content) self.send_html(content)
@ -405,11 +381,7 @@ class FileServerHandler(BaseHTTPRequestHandler):
<html> <html>
<head> <head>
<title>Explore Files - SimpliestFS</title> <title>Explore Files - SimpliestFS</title>
<style> <link rel="stylesheet" href="/static/style.css">
body {{ font-family: sans-serif; margin: 40px; font-size: 1.5em; }}
.back-btn {{ display: inline-block; padding: 5px 10px; background: #eee; border: 1px solid #ccc; text-decoration: none; color: #333; }}
h1 a {{ text-decoration: none; color: inherit; }}
</style>
</head> </head>
<body> <body>
<h1><a href="/">SimpliestFS</a></h1> <h1><a href="/">SimpliestFS</a></h1>
@ -448,11 +420,7 @@ class FileServerHandler(BaseHTTPRequestHandler):
<html> <html>
<head> <head>
<title>{username}'s Files - SimpliestFS</title> <title>{username}'s Files - SimpliestFS</title>
<style> <link rel="stylesheet" href="/static/style.css">
body {{ font-family: sans-serif; margin: 40px; font-size: 1em; }}
.back-btn {{ display: inline-block; padding: 5px 10px; background: #eee; border: 1px solid #ccc; text-decoration: none; color: #333; }}
h1 a {{ text-decoration: none; color: inherit; }}
</style>
</head> </head>
<body> <body>
<h1><a href="/">SimpliestFS</a></h1> <h1><a href="/">SimpliestFS</a></h1>
@ -573,6 +541,41 @@ class FileServerHandler(BaseHTTPRequestHandler):
database.DB_CONN.commit() database.DB_CONN.commit()
self.send_html("<h1>Account deleted</h1>") self.send_html("<h1>Account deleted</h1>")
def handle_static_file(self):
if self.path.startswith("/static/"):
file_path = self.path[1:] # Remove leading slash
try:
with open(file_path, "r") as file:
content = file.read()
if file_path.endswith(".css"):
self.send_response(200)
self.send_header("Content-type", "text/css")
self.end_headers()
self.wfile.write(content.encode())
else:
self.send_error(404)
except FileNotFoundError:
self.send_error(404)
return True
return False
def handle_favicon(self):
if self.path == "/favicon.ico":
file_path = os.path.join("static", self.path[1:])
try:
with open(file_path, "rb") as file: # Open in binary mode
content = file.read()
self.send_response(200)
self.send_header(
"Content-type", "image/x-icon"
) # Correct MIME type
self.end_headers()
self.wfile.write(content) # Write bytes directly
except FileNotFoundError:
self.send_error(404)
return True
return False
def do_GET(self): def do_GET(self):
parsed_path = urlparse(self.path) parsed_path = urlparse(self.path)
path = parsed_path.path path = parsed_path.path
@ -597,6 +600,10 @@ class FileServerHandler(BaseHTTPRequestHandler):
else: else:
username = parts[0] username = parts[0]
self.handle_explore_user(username) self.handle_explore_user(username)
elif path.startswith("/static/") and not path.endswith("/"):
self.handle_static_file()
elif path == "/favicon.ico":
self.handle_favicon()
else: else:
self.send_html("<h1>Not Found</h1>", 404) self.send_html("<h1>Not Found</h1>", 404)

BIN
static/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

332
static/style.css Normal file
View File

@ -0,0 +1,332 @@
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
Arial, sans-serif;
margin: 0;
padding: 20px;
font-size: 16px;
line-height: 1.6;
color: #2c3e50;
background-color: #f8f9fa;
min-height: 100vh;
}
.container {
max-width: 900px;
margin: 0 auto;
}
h1 {
text-align: center;
margin: 30px 0;
font-size: 32px;
font-weight: 600;
color: #1a1a1a;
}
h1 a {
text-decoration: none;
color: inherit;
transition: color 0.2s;
}
h1 a:hover {
color: #4a5568;
}
h2 {
font-size: 24px;
font-weight: 500;
margin: 20px 0;
color: #2d3748;
text-align: center;
}
p {
margin: 12px 0;
color: #4a5568;
}
a {
color: #2c5282;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
hr {
border: none;
border-top: 1px solid #e2e8f0;
margin: 30px 0;
}
.btn,
.back-btn,
.upload-btn,
.regular-button,
button[type="submit"] {
display: inline-block;
padding: 12px 24px;
background: #ffffff;
border: 1px solid #cbd5e0;
border-radius: 6px;
text-decoration: none;
color: #2d3748;
font-size: 15px;
font-weight: 500;
text-align: center;
cursor: pointer;
transition: all 0.2s;
font-family: inherit;
}
.btn:hover,
.back-btn:hover,
.upload-btn:hover,
.regular-button:hover,
button[type="submit"]:hover {
background: #f7fafc;
border-color: #a0aec0;
text-decoration: none;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.back-btn,
.upload-btn {
margin: 5px;
}
.main-page {
text-align: center;
max-width: 700px;
margin: 0 auto;
}
.main-page h1 {
font-size: 40px;
margin-bottom: 40px;
}
.main-page p {
margin: 20px auto;
max-width: 600px;
font-size: 15px;
}
.main-page .btn {
font-size: 16px;
padding: 14px 32px;
margin: 8px;
min-width: 160px;
}
.main-page .disclaimer {
margin-top: 50px;
padding: 20px;
background-color: #edf2f7;
border-radius: 8px;
border: 1px solid #cbd5e0;
font-size: 14px;
color: #4a5568;
}
.error {
color: #c53030;
background-color: #fff5f5;
padding: 12px 16px;
border-radius: 6px;
border: 1px solid #feb2b2;
margin: 20px 0;
text-align: center;
}
.quota-info {
margin: 25px 0;
padding: 12px 20px;
font-size: 14px;
color: #4a5568;
text-align: center;
background-color: #edf2f7;
border-radius: 6px;
border: 1px solid #cbd5e0;
}
.login-form {
max-width: 420px;
margin: 30px auto;
padding: 30px;
background-color: #ffffff;
border-radius: 8px;
border: 1px solid #e2e8f0;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}
.login-form input {
width: 100%;
padding: 14px 16px;
margin: 10px 0;
font-size: 15px;
border: 1px solid #cbd5e0;
border-radius: 6px;
background-color: #ffffff;
font-family: inherit;
}
.login-form input:focus {
outline: none;
border-color: #4299e1;
box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.1);
}
.login-form button {
width: 100%;
margin-top: 10px;
}
form[action="/upload"] {
margin: 25px 0;
padding: 20px;
background-color: #ffffff;
border-radius: 8px;
border: 1px solid #e2e8f0;
text-align: center;
}
input[type="file"] {
padding: 10px;
margin: 10px;
border: 1px solid #cbd5e0;
border-radius: 6px;
background-color: #ffffff;
cursor: pointer;
}
.files-list-item,
div[style*="margin: 5px 0"] {
margin: 12px 0 !important;
padding: 14px 16px !important;
display: flex !important;
align-items: center !important;
border: 1px solid #e2e8f0 !important;
border-radius: 6px !important;
background-color: #ffffff !important;
}
.files-list-item:hover,
div[style*="margin: 5px 0"]:hover {
background-color: #f7fafc !important;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05) !important;
}
.files-list-item span,
div[style*="margin: 5px 0"] span {
flex: 1 !important;
text-align: left !important;
}
.files-list-item a,
div[style*="margin: 5px 0"] a {
font-weight: 500;
color: #2c5282;
word-break: break-all;
}
.files-list-item form,
div[style*="margin: 5px 0"] form {
display: inline !important;
margin-left: 10px !important;
}
.files-list-item button,
div[style*="margin: 5px 0"] button {
padding: 8px 16px !important;
font-size: 14px !important;
margin: 0 !important;
}
div[style*="margin: 5px 0"] a:only-child {
display: block;
padding: 12px 16px;
font-weight: 500;
}
p a.back-btn {
display: inline-block;
margin: 10px 5px;
}
@media (max-width: 768px) {
body {
padding: 15px;
font-size: 15px;
}
h1 {
font-size: 28px;
margin: 20px 0;
}
h2 {
font-size: 22px;
}
.main-page h1 {
font-size: 32px;
}
.btn,
.back-btn,
.upload-btn,
button[type="submit"] {
padding: 14px 20px;
font-size: 15px;
}
.main-page .btn {
width: 100%;
max-width: 300px;
display: block;
margin: 10px auto;
}
.login-form {
padding: 25px 20px;
}
form[action="/upload"] {
padding: 15px;
}
input[type="file"] {
width: 100%;
margin: 10px 0;
}
form[action="/upload"] button {
width: 100%;
margin-top: 10px;
}
}
@media (max-width: 480px) {
body {
padding: 10px;
}
h1 {
font-size: 24px;
margin: 15px 0;
}
h2 {
font-size: 20px;
}
.main-page h1 {
font-size: 28px;
}
.main-page p {
font-size: 14px;
}
.btn,
.back-btn,
.upload-btn,
button[type="submit"] {
padding: 12px 16px;
font-size: 14px;
}
.main-page .btn {
font-size: 15px;
padding: 14px 24px;
}
.login-form {
padding: 20px 15px;
}
.files-list-item,
div[style*="margin: 5px 0"] {
flex-direction: column !important;
align-items: flex-start !important;
padding: 12px !important;
}
.files-list-item span,
div[style*="margin: 5px 0"] span {
margin-bottom: 10px !important;
width: 100% !important;
}
.files-list-item form,
div[style*="margin: 5px 0"] form {
width: 100% !important;
margin-left: 0 !important;
margin-top: 8px !important;
}
.files-list-item button,
div[style*="margin: 5px 0"] button {
width: 100% !important;
}
p a.back-btn {
display: block;
margin: 8px 0;
}
}