login logo

This commit is contained in:
DigiServer Developer
2025-11-17 22:32:37 +02:00
parent c16383ed75
commit 6d44542765
9 changed files with 514 additions and 34 deletions

View File

@@ -567,6 +567,27 @@ def dependencies():
except Exception:
pass
# Check Emoji Fonts
emoji_installed = False
emoji_version = 'Not installed'
try:
result = subprocess.run(['dpkg', '-l', 'fonts-noto-color-emoji'],
capture_output=True,
text=True,
timeout=5)
if result.returncode == 0 and 'ii' in result.stdout:
emoji_installed = True
# Get version from dpkg output
lines = result.stdout.split('\n')
for line in lines:
if 'fonts-noto-color-emoji' in line and line.startswith('ii'):
parts = line.split()
if len(parts) >= 3:
emoji_version = f'Noto Color Emoji {parts[2]}'
break
except Exception:
pass
return render_template('admin/dependencies.html',
libreoffice_installed=libreoffice_installed,
libreoffice_version=libreoffice_version,
@@ -613,3 +634,111 @@ def install_libreoffice():
flash(f'Error: {str(e)}', 'danger')
return redirect(url_for('admin.dependencies'))
@admin_bp.route('/install-emoji-fonts', methods=['POST'])
@login_required
@admin_required
def install_emoji_fonts():
"""Install Emoji Fonts for better UI display."""
import subprocess
try:
# Run installation script
script_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))),
'install_emoji_fonts.sh')
if not os.path.exists(script_path):
flash('Installation script not found', 'danger')
return redirect(url_for('admin.dependencies'))
result = subprocess.run(['sudo', 'bash', script_path],
capture_output=True,
text=True,
timeout=180)
if result.returncode == 0:
log_action('info', 'Emoji fonts installed successfully')
flash('Emoji fonts installed successfully! Please restart your browser to see changes.', 'success')
else:
log_action('error', f'Emoji fonts installation failed: {result.stderr}')
flash(f'Installation failed: {result.stderr}', 'danger')
except subprocess.TimeoutExpired:
flash('Installation timeout. Please try again.', 'warning')
except Exception as e:
log_action('error', f'Error installing emoji fonts: {str(e)}')
flash(f'Error: {str(e)}', 'danger')
return redirect(url_for('admin.dependencies'))
@admin_bp.route('/customize-logos')
@login_required
@admin_required
def customize_logos():
"""Logo customization page."""
import time
return render_template('admin/customize_logos.html', version=int(time.time()))
@admin_bp.route('/upload-header-logo', methods=['POST'])
@login_required
@admin_required
def upload_header_logo():
"""Upload header logo."""
try:
if 'header_logo' not in request.files:
flash('No file selected', 'warning')
return redirect(url_for('admin.customize_logos'))
file = request.files['header_logo']
if file.filename == '':
flash('No file selected', 'warning')
return redirect(url_for('admin.customize_logos'))
if file:
# Save as header_logo.png
filename = 'header_logo.png'
filepath = os.path.join(current_app.config['UPLOAD_FOLDER'], filename)
file.save(filepath)
log_action('info', f'Header logo uploaded: {filename}')
flash('Header logo uploaded successfully!', 'success')
except Exception as e:
log_action('error', f'Error uploading header logo: {str(e)}')
flash(f'Error uploading logo: {str(e)}', 'danger')
return redirect(url_for('admin.customize_logos'))
@admin_bp.route('/upload-login-logo', methods=['POST'])
@login_required
@admin_required
def upload_login_logo():
"""Upload login page logo."""
try:
if 'login_logo' not in request.files:
flash('No file selected', 'warning')
return redirect(url_for('admin.customize_logos'))
file = request.files['login_logo']
if file.filename == '':
flash('No file selected', 'warning')
return redirect(url_for('admin.customize_logos'))
if file:
# Save as login_logo.png
filename = 'login_logo.png'
filepath = os.path.join(current_app.config['UPLOAD_FOLDER'], filename)
file.save(filepath)
log_action('info', f'Login logo uploaded: {filename}')
flash('Login logo uploaded successfully!', 'success')
except Exception as e:
log_action('error', f'Error uploading login logo: {str(e)}')
flash(f'Error uploading logo: {str(e)}', 'danger')
return redirect(url_for('admin.customize_logos'))

View File

@@ -18,7 +18,7 @@ class Config:
# File Upload - use absolute paths
MAX_CONTENT_LENGTH = 2048 * 1024 * 1024 # 2GB
_basedir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
_basedir = os.path.abspath(os.path.dirname(__file__))
UPLOAD_FOLDER = os.path.join(_basedir, 'static', 'uploads')
UPLOAD_FOLDERLOGO = os.path.join(_basedir, 'static', 'resurse')
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'bmp', 'mp4', 'avi', 'mkv', 'mov', 'webm', 'pdf', 'ppt', 'pptx'}

View File

@@ -81,6 +81,17 @@
</div>
</div>
<!-- Logo Customization Card -->
<div class="card management-card" style="background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);">
<h2>🎨 Logo Customization</h2>
<p>Upload custom logos for header and login page</p>
<div class="card-actions">
<a href="{{ url_for('admin.customize_logos') }}" class="btn btn-primary">
Customize Logos
</a>
</div>
</div>
<!-- Quick Actions Card -->
<div class="card">
<h2>⚡ Quick Actions</h2>

View File

@@ -0,0 +1,101 @@
{% extends "base.html" %}
{% block title %}Logo Customization - DigiServer{% endblock %}
{% block content %}
<div class="container" style="max-width: 900px;">
<h1 style="margin-bottom: 25px;">🎨 Logo Customization</h1>
<div class="card" style="margin-bottom: 20px;">
<h2 style="margin-bottom: 20px;">📸 Upload Custom Logos</h2>
<!-- Header Logo -->
<div style="margin-bottom: 30px; padding: 20px; background: #f8f9fa; border-radius: 8px;">
<h3 style="margin-bottom: 15px;">Header Logo (Small)</h3>
<p style="color: #666; margin-bottom: 15px;">
This logo appears in the top header next to "DigiServer" text.<br>
<strong>Recommended:</strong> 150x40 pixels (or similar aspect ratio), transparent background PNG
</p>
<div style="display: flex; align-items: center; gap: 20px; margin-bottom: 15px;">
<div style="flex: 1;">
<img src="{{ url_for('static', filename='uploads/header_logo.png') }}?v={{ version }}"
alt="Current Header Logo"
style="max-height: 50px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 10px; border-radius: 4px;"
onerror="this.src='{{ url_for('static', filename='icons/monitor.svg') }}'; this.style.filter='brightness(0) invert(1)'; this.style.maxWidth='50px';">
<p style="margin-top: 5px; font-size: 0.9rem; color: #888;">Current Header Logo</p>
</div>
</div>
<form method="POST" action="{{ url_for('admin.upload_header_logo') }}" enctype="multipart/form-data">
<div style="margin-bottom: 10px;">
<input type="file" name="header_logo" accept="image/png,image/jpeg,image/svg+xml" required
style="padding: 10px; border: 2px solid #ddd; border-radius: 4px; width: 100%;">
</div>
<button type="submit" class="btn btn-primary">📤 Upload Header Logo</button>
</form>
</div>
<!-- Login Logo -->
<div style="margin-bottom: 30px; padding: 20px; background: #f8f9fa; border-radius: 8px;">
<h3 style="margin-bottom: 15px;">Login Page Logo (Large)</h3>
<p style="color: #666; margin-bottom: 15px;">
This logo appears on the left side of the login page (2/3 of screen).<br>
<strong>Recommended:</strong> 800x600 pixels (or similar), transparent background PNG
</p>
<div style="display: flex; align-items: center; gap: 20px; margin-bottom: 15px;">
<div style="flex: 1;">
<img src="{{ url_for('static', filename='uploads/login_logo.png') }}?v={{ version }}"
alt="Current Login Logo"
style="max-width: 300px; max-height: 200px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 20px; border-radius: 8px;"
onerror="this.style.display='none'; this.nextElementSibling.style.display='block';">
<p style="margin-top: 10px; font-size: 0.9rem; color: #888; display: none;">No login logo uploaded yet</p>
</div>
</div>
<form method="POST" action="{{ url_for('admin.upload_login_logo') }}" enctype="multipart/form-data">
<div style="margin-bottom: 10px;">
<input type="file" name="login_logo" accept="image/png,image/jpeg,image/svg+xml" required
style="padding: 10px; border: 2px solid #ddd; border-radius: 4px; width: 100%;">
</div>
<button type="submit" class="btn btn-primary">📤 Upload Login Logo</button>
</form>
</div>
<div style="padding: 15px; background: #e7f3ff; border-radius: 8px; border-left: 4px solid #0066cc;">
<h4 style="margin: 0 0 10px 0;"> Logo Guidelines</h4>
<ul style="margin: 5px 0; padding-left: 25px; color: #555;">
<li><strong>Header Logo:</strong> Keep it simple and small (max 200px width recommended)</li>
<li><strong>Login Logo:</strong> Can be larger and more detailed (800x600px works great)</li>
<li><strong>Format:</strong> PNG with transparent background recommended, or JPG/SVG</li>
<li><strong>File Size:</strong> Keep under 2MB for optimal performance</li>
<li>Logos are cached - clear browser cache if changes don't appear immediately</li>
</ul>
</div>
</div>
<div style="margin-top: 20px;">
<a href="{{ url_for('admin.admin_panel') }}" class="btn btn-secondary">
← Back to Admin Panel
</a>
</div>
</div>
<style>
body.dark-mode div[style*="background: #f8f9fa"] {
background: #2d3748 !important;
}
body.dark-mode div[style*="background: #e7f3ff"] {
background: #1e3a5f !important;
border-left-color: #64b5f6 !important;
}
body.dark-mode p[style*="color: #666"],
body.dark-mode p[style*="color: #888"],
body.dark-mode ul[style*="color: #555"] {
color: #cbd5e0 !important;
}
</style>
{% endblock %}

View File

@@ -87,11 +87,46 @@
</div>
</div>
<!-- Emoji Fonts -->
<div class="dependency-card" style="background: {% if emoji_installed %}#d4edda{% else %}#fff3cd{% endif %}; padding: 20px; border-radius: 8px; margin-bottom: 15px; border-left: 4px solid {% if emoji_installed %}#28a745{% else %}#ffc107{% endif %};">
<div style="display: flex; justify-content: space-between; align-items: start;">
<div style="flex: 1;">
<h3 style="margin: 0 0 10px 0; display: flex; align-items: center; gap: 10px;">
{% if emoji_installed %}
<span style="font-size: 24px;"></span>
{% else %}
<span style="font-size: 24px;">⚠️</span>
{% endif %}
Emoji Fonts
</h3>
<p style="margin: 5px 0; color: #555;">
<strong>Purpose:</strong> Better emoji display in UI (optional, mainly for Raspberry Pi)
</p>
<p style="margin: 5px 0; color: #555;">
<strong>Status:</strong> {{ emoji_version }}
</p>
{% if not emoji_installed %}
<p style="margin: 10px 0 0 0; color: #856404;">
Optional: Improves emoji rendering on systems without native emoji support.
</p>
{% endif %}
</div>
{% if not emoji_installed %}
<form method="POST" action="{{ url_for('admin.install_emoji_fonts') }}" style="margin-left: 20px;">
<button type="submit" class="btn btn-warning" onclick="return confirm('Install emoji fonts? This may take 1-2 minutes.');">
📥 Install Emoji Fonts
</button>
</form>
{% endif %}
</div>
</div>
<div style="margin-top: 25px; padding: 15px; background: #e7f3ff; border-radius: 8px; border-left: 4px solid #0066cc;">
<h4 style="margin: 0 0 10px 0;"> Installation Notes</h4>
<ul style="margin: 5px 0; padding-left: 25px; color: #555;">
<li>LibreOffice can be installed using the button above (requires sudo access)</li>
<li>Installation may take 2-5 minutes depending on your internet connection</li>
<li>Emoji fonts improve UI display, especially on Raspberry Pi systems</li>
<li>Installation may take 1-5 minutes depending on your internet connection</li>
<li>After installation, refresh this page to verify the status</li>
<li>Docker containers may require rebuilding to include dependencies</li>
</ul>

View File

@@ -1,31 +1,233 @@
{% extends "base.html" %}
{% block title %}Login - DigiServer v2{% endblock %}
{% block content %}
<div class="card" style="max-width: 400px; margin: 2rem auto;">
<h2>Login</h2>
<form method="POST" action="{{ url_for('auth.login') }}">
<div style="margin-bottom: 1rem;">
<label for="username" style="display: block; margin-bottom: 0.5rem;">Username</label>
<input type="text" id="username" name="username" required
style="width: 100%; padding: 0.5rem; border: 1px solid #ddd; border-radius: 4px;">
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - DigiServer</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
height: 100vh;
display: flex;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.login-container {
display: flex;
width: 100%;
height: 100vh;
}
.logo-section {
flex: 2;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
}
.logo-section img {
max-width: 70%;
max-height: 70%;
object-fit: contain;
filter: drop-shadow(0 10px 30px rgba(0, 0, 0, 0.3));
}
.form-section {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
background: white;
padding: 2rem;
}
.login-form {
width: 100%;
max-width: 400px;
}
.login-form h2 {
color: #2d3748;
margin-bottom: 2rem;
font-size: 2rem;
text-align: center;
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
color: #4a5568;
font-weight: 500;
}
.form-group input[type="text"],
.form-group input[type="password"] {
width: 100%;
padding: 0.75rem;
border: 2px solid #e2e8f0;
border-radius: 8px;
font-size: 1rem;
transition: border-color 0.3s;
}
.form-group input[type="text"]:focus,
.form-group input[type="password"]:focus {
outline: none;
border-color: #667eea;
}
.remember-me {
display: flex;
align-items: center;
margin-bottom: 1.5rem;
}
.remember-me input {
margin-right: 0.5rem;
}
.remember-me label {
color: #4a5568;
cursor: pointer;
}
.btn-login {
width: 100%;
padding: 0.75rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
}
.btn-login:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(102, 126, 234, 0.4);
}
.register-link {
margin-top: 1.5rem;
text-align: center;
color: #718096;
}
.register-link a {
color: #667eea;
text-decoration: none;
font-weight: 600;
}
.register-link a:hover {
text-decoration: underline;
}
.flash-messages {
margin-bottom: 1.5rem;
}
.alert {
padding: 0.75rem;
border-radius: 8px;
margin-bottom: 0.5rem;
}
.alert-danger {
background: #fed7d7;
color: #c53030;
border: 1px solid #fc8181;
}
.alert-success {
background: #c6f6d5;
color: #2f855a;
border: 1px solid #68d391;
}
.alert-warning {
background: #feebc8;
color: #c05621;
border: 1px solid #f6ad55;
}
@media (max-width: 768px) {
.login-container {
flex-direction: column;
}
.logo-section {
flex: 1;
min-height: 200px;
}
.form-section {
flex: 2;
}
}
</style>
</head>
<body>
<div class="login-container">
<!-- Logo Section (Left - 2/3) -->
<div class="logo-section">
<img src="{{ url_for('static', filename='uploads/login_logo.png') }}?v={{ range(1, 999999) | random }}"
alt="DigiServer Logo"
onerror="this.style.display='none';">
</div>
<div style="margin-bottom: 1rem;">
<label for="password" style="display: block; margin-bottom: 0.5rem;">Password</label>
<input type="password" id="password" name="password" required
style="width: 100%; padding: 0.5rem; border: 1px solid #ddd; border-radius: 4px;">
<!-- Form Section (Right - 1/3) -->
<div class="form-section">
<div class="login-form">
<h2>Welcome Back</h2>
<!-- Flash Messages -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class="flash-messages">
{% for category, message in messages %}
<div class="alert alert-{{ category }}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<form method="POST" action="{{ url_for('auth.login') }}">
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" name="username" required autofocus>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" required>
</div>
<div class="remember-me">
<input type="checkbox" id="remember" name="remember" value="yes">
<label for="remember">Remember me</label>
</div>
<button type="submit" class="btn-login">Login</button>
</form>
<div class="register-link">
Don't have an account? <a href="{{ url_for('auth.register') }}">Register here</a>
</div>
</div>
</div>
<div style="margin-bottom: 1rem;">
<label>
<input type="checkbox" name="remember" value="yes">
Remember me
</label>
</div>
<button type="submit" class="btn" style="width: 100%;">Login</button>
</form>
<p style="margin-top: 1rem; text-align: center;">
Don't have an account? <a href="{{ url_for('auth.register') }}">Register here</a>
</p>
</div>
{% endblock %}
</div>
</body>
</html>

View File

@@ -310,8 +310,8 @@
<header>
<div class="container">
<h1>
<img src="{{ url_for('static', filename='icons/monitor.svg') }}" alt="DigiServer" style="width: 28px; height: 28px; filter: brightness(0) invert(1);">
DigiServer v2
<img src="{{ url_for('static', filename='uploads/header_logo.png') }}" alt="DigiServer" style="height: 32px; width: auto;" onerror="this.src='{{ url_for('static', filename='icons/monitor.svg') }}'; this.style.filter='brightness(0) invert(1)'; this.style.width='28px'; this.style.height='28px';">
DigiServer
</h1>
<nav>
{% if current_user.is_authenticated %}