login logo
This commit is contained in:
@@ -20,6 +20,7 @@ ENV/
|
|||||||
*.sh
|
*.sh
|
||||||
!docker-entrypoint.sh
|
!docker-entrypoint.sh
|
||||||
!install_libreoffice.sh
|
!install_libreoffice.sh
|
||||||
|
!install_emoji_fonts.sh
|
||||||
|
|
||||||
# Database (will be created in volume)
|
# Database (will be created in volume)
|
||||||
instance/
|
instance/
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ EXPOSE 5000
|
|||||||
RUN useradd -m -u 1000 appuser && \
|
RUN useradd -m -u 1000 appuser && \
|
||||||
chown -R appuser:appuser /app /docker-entrypoint.sh && \
|
chown -R appuser:appuser /app /docker-entrypoint.sh && \
|
||||||
echo "appuser ALL=(ALL) NOPASSWD: /app/install_libreoffice.sh" >> /etc/sudoers && \
|
echo "appuser ALL=(ALL) NOPASSWD: /app/install_libreoffice.sh" >> /etc/sudoers && \
|
||||||
chmod +x /app/install_libreoffice.sh
|
echo "appuser ALL=(ALL) NOPASSWD: /app/install_emoji_fonts.sh" >> /etc/sudoers && \
|
||||||
|
chmod +x /app/install_libreoffice.sh /app/install_emoji_fonts.sh
|
||||||
|
|
||||||
USER appuser
|
USER appuser
|
||||||
|
|
||||||
|
|||||||
@@ -567,6 +567,27 @@ def dependencies():
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
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',
|
return render_template('admin/dependencies.html',
|
||||||
libreoffice_installed=libreoffice_installed,
|
libreoffice_installed=libreoffice_installed,
|
||||||
libreoffice_version=libreoffice_version,
|
libreoffice_version=libreoffice_version,
|
||||||
@@ -613,3 +634,111 @@ def install_libreoffice():
|
|||||||
flash(f'Error: {str(e)}', 'danger')
|
flash(f'Error: {str(e)}', 'danger')
|
||||||
|
|
||||||
return redirect(url_for('admin.dependencies'))
|
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'))
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class Config:
|
|||||||
|
|
||||||
# File Upload - use absolute paths
|
# File Upload - use absolute paths
|
||||||
MAX_CONTENT_LENGTH = 2048 * 1024 * 1024 # 2GB
|
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_FOLDER = os.path.join(_basedir, 'static', 'uploads')
|
||||||
UPLOAD_FOLDERLOGO = os.path.join(_basedir, 'static', 'resurse')
|
UPLOAD_FOLDERLOGO = os.path.join(_basedir, 'static', 'resurse')
|
||||||
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'bmp', 'mp4', 'avi', 'mkv', 'mov', 'webm', 'pdf', 'ppt', 'pptx'}
|
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'bmp', 'mp4', 'avi', 'mkv', 'mov', 'webm', 'pdf', 'ppt', 'pptx'}
|
||||||
|
|||||||
@@ -81,6 +81,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</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 -->
|
<!-- Quick Actions Card -->
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h2>⚡ Quick Actions</h2>
|
<h2>⚡ Quick Actions</h2>
|
||||||
|
|||||||
101
app/templates/admin/customize_logos.html
Normal file
101
app/templates/admin/customize_logos.html
Normal 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 %}
|
||||||
@@ -87,11 +87,46 @@
|
|||||||
</div>
|
</div>
|
||||||
</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;">
|
<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>
|
<h4 style="margin: 0 0 10px 0;">ℹ️ Installation Notes</h4>
|
||||||
<ul style="margin: 5px 0; padding-left: 25px; color: #555;">
|
<ul style="margin: 5px 0; padding-left: 25px; color: #555;">
|
||||||
<li>LibreOffice can be installed using the button above (requires sudo access)</li>
|
<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>After installation, refresh this page to verify the status</li>
|
||||||
<li>Docker containers may require rebuilding to include dependencies</li>
|
<li>Docker containers may require rebuilding to include dependencies</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -1,31 +1,233 @@
|
|||||||
{% extends "base.html" %}
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
{% block title %}Login - DigiServer v2{% endblock %}
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
{% block content %}
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<div class="card" style="max-width: 400px; margin: 2rem auto;">
|
<title>Login - DigiServer</title>
|
||||||
<h2>Login</h2>
|
<style>
|
||||||
<form method="POST" action="{{ url_for('auth.login') }}">
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
<div style="margin-bottom: 1rem;">
|
|
||||||
<label for="username" style="display: block; margin-bottom: 0.5rem;">Username</label>
|
body {
|
||||||
<input type="text" id="username" name="username" required
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||||
style="width: 100%; padding: 0.5rem; border: 1px solid #ddd; border-radius: 4px;">
|
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>
|
||||||
<div style="margin-bottom: 1rem;">
|
|
||||||
<label for="password" style="display: block; margin-bottom: 0.5rem;">Password</label>
|
<!-- Form Section (Right - 1/3) -->
|
||||||
<input type="password" id="password" name="password" required
|
<div class="form-section">
|
||||||
style="width: 100%; padding: 0.5rem; border: 1px solid #ddd; border-radius: 4px;">
|
<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>
|
||||||
<div style="margin-bottom: 1rem;">
|
</div>
|
||||||
<label>
|
</body>
|
||||||
<input type="checkbox" name="remember" value="yes">
|
</html>
|
||||||
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 %}
|
|
||||||
|
|||||||
@@ -310,8 +310,8 @@
|
|||||||
<header>
|
<header>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>
|
<h1>
|
||||||
<img src="{{ url_for('static', filename='icons/monitor.svg') }}" alt="DigiServer" style="width: 28px; height: 28px; filter: brightness(0) invert(1);">
|
<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 v2
|
DigiServer
|
||||||
</h1>
|
</h1>
|
||||||
<nav>
|
<nav>
|
||||||
{% if current_user.is_authenticated %}
|
{% if current_user.is_authenticated %}
|
||||||
|
|||||||
Reference in New Issue
Block a user