Compare commits

...

5 Commits

7 changed files with 157 additions and 21 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
NUITKA_BUILD=0
INVENTAR_HTTP_PORT=10000
INVENTAR_HTTP_PORTS=10000
INVENTAR_APP_IMAGE=ghcr.io/aiirondev/legendary-octo-garbanzo:v0.7.39
INVENTAR_APP_IMAGE=ghcr.io/aiirondev/legendary-octo-garbanzo:v0.7.42
INVENTAR_APP_CPUS=1.0
INVENTAR_APP_MEM_LIMIT=384m
INVENTAR_APP_MEM_SWAP_LIMIT=768m
+24 -1
View File
@@ -25,9 +25,10 @@ Features:
- Booking and reservation of items
"""
from flask import Flask, render_template, request, redirect, url_for, session, flash, send_from_directory, get_flashed_messages, jsonify, Response, make_response, send_file
from flask import Flask, render_template, request, redirect, url_for, session, flash, send_from_directory, get_flashed_messages, jsonify, Response, make_response, send_file, abort
from werkzeug.utils import secure_filename
from werkzeug.middleware.proxy_fix import ProxyFix
from jinja2 import TemplateNotFound
import user as us
import items as it
import ausleihung as au
@@ -412,6 +413,26 @@ def _set_security_headers(response):
return response
@app.errorhandler(TemplateNotFound)
def handle_template_not_found(e):
"""Handle missing template files with a 404 response."""
app.logger.error(f"Template not found: {e.name}")
if request.is_json or request.path.startswith('/api/'):
return jsonify({'error': 'Template not found', 'status': 404}), 404
return abort(404)
@app.errorhandler(404)
def handle_not_found(e):
"""Handle 404 errors with a user-friendly response."""
app.logger.warning(f"404 Not Found: {request.path}")
if request.is_json or request.path.startswith('/api/'):
return jsonify({'error': 'Not found', 'status': 404}), 404
if 'username' in session:
return render_template('error.html', error_code=404, error_message='Seite nicht gefunden.'), 404
return redirect(url_for('login'))
def _csrf_error_response(message='CSRF token fehlt oder ist ungültig.'):
if request.is_json or request.path.startswith('/api/') or request.path in {'/download_book_cover', '/proxy_image', '/log_mobile_issue'}:
return jsonify({'error': message}), 400
@@ -8566,11 +8587,13 @@ def manage_filters():
# Get predefined filter values
filter1_values = it.get_predefined_filter_values(1)
filter2_values = it.get_predefined_filter_values(2)
filter3_values = it.get_predefined_filter_values(3)
return render_template(
'manage_filters.html',
filter1_values=filter1_values,
filter2_values=filter2_values,
filter3_values=filter3_values,
library_module_enabled=cfg.MODULES.is_enabled('library'),
student_cards_module_enabled=cfg.MODULES.is_enabled('student_cards')
)
+12 -3
View File
@@ -570,7 +570,10 @@ def get_primary_filters():
items = db['items']
filters = [f for f in items.distinct('Filter', _active_record_query(_non_library_query())) if f]
client.close()
return filters
# Add predefined values
predefined = get_predefined_filter_values(1)
return sorted(list(set(filters + predefined)))
except Exception as e:
print(f"Error retrieving primary filters: {e}")
return []
@@ -589,7 +592,10 @@ def get_secondary_filters():
items = db['items']
filters = [f for f in items.distinct('Filter2', _active_record_query(_non_library_query())) if f]
client.close()
return filters
# Add predefined values
predefined = get_predefined_filter_values(2)
return sorted(list(set(filters + predefined)))
except Exception as e:
print(f"Error retrieving secondary filters: {e}")
return []
@@ -608,7 +614,10 @@ def get_tertiary_filters():
items = db['items']
filters = [f for f in items.distinct('Filter3', _active_record_query(_non_library_query())) if f]
client.close()
return filters
# Add predefined values
predefined = get_predefined_filter_values(3)
return sorted(list(set(filters + predefined)))
except Exception as e:
print(f"Error retrieving tertiary filters: {e}")
return []
+75
View File
@@ -0,0 +1,75 @@
{% extends "base.html" %}
{% block title %}
{% if error_code == 404 %}
Seite nicht gefunden - Inventarsystem
{% else %}
Fehler - Inventarsystem
{% endif %}
{% endblock %}
{% block content %}
<div class="error-container" style="text-align: center; padding: 60px 20px; min-height: 70vh; display: flex; flex-direction: column; justify-content: center; align-items: center;">
<div style="max-width: 600px;">
<!-- Error Code Display -->
<div style="font-size: 120px; font-weight: bold; color: #ef4444; margin-bottom: 20px;">
{{ error_code }}
</div>
<!-- Error Title -->
<h1 style="font-size: 32px; margin: 20px 0; color: #1f2937;">
{% if error_code == 404 %}
Seite nicht gefunden
{% else %}
Ein Fehler ist aufgetreten
{% endif %}
</h1>
<!-- Error Message -->
<p style="font-size: 18px; color: #6b7280; margin: 20px 0 40px;">
{{ error_message or 'Es tut uns leid, aber die angeforderte Seite konnte nicht gefunden werden.' }}
</p>
<!-- Action Buttons -->
<div style="display: flex; gap: 15px; justify-content: center; flex-wrap: wrap;">
<a href="{{ url_for('home') }}" class="button" style="background-color: #3b82f6; color: white; padding: 12px 30px; text-decoration: none; border-radius: 5px; display: inline-block; font-weight: 500;">
Zur Startseite
</a>
<a href="javascript:history.back()" class="button" style="background-color: #6b7280; color: white; padding: 12px 30px; text-decoration: none; border-radius: 5px; display: inline-block; font-weight: 500;">
Zurück
</a>
</div>
<!-- Additional Help -->
<div style="margin-top: 60px; padding: 20px; background-color: #f3f4f6; border-radius: 8px;">
<p style="color: #6b7280; font-size: 14px;">
Wenn Sie denken, dass dies ein Fehler ist, oder Sie Hilfe benötigen,
<a href="{{ url_for('impressum') }}" style="color: #3b82f6; text-decoration: none;">kontaktieren Sie den Administrator</a>.
</p>
</div>
</div>
</div>
<style>
.error-container {
animation: fadeIn 0.3s ease-in;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.button:hover {
opacity: 0.9;
transform: translateY(-2px);
transition: all 0.2s ease;
}
</style>
{% endblock %}
+44 -8
View File
@@ -15,16 +15,16 @@
<h1 class="mb-4">Filterwerte verwalten</h1>
<div class="row">
<!-- Filter 1: Unterrichtsfach -->
<div class="col-md-6">
<!-- Filter 1 -->
<div class="col-md-4">
<div class="card mb-4">
<div class="card-header">
<h2 class="card-title h5 mb-0">Unterrichtsfach (Filter 1)</h2>
<h2 class="card-title h5 mb-0">Fach/Kategorie (Filter 1)</h2>
</div>
<div class="card-body">
<form method="POST" action="{{ url_for('add_filter_value', filter_num=1) }}" class="mb-4">
<div class="input-group">
<input type="text" name="value" class="form-control" placeholder="Neues Unterrichtsfach..." required>
<input type="text" name="value" class="form-control" placeholder="Neuer Wert..." required>
<button type="submit" class="btn btn-primary">Hinzufügen</button>
</div>
</form>
@@ -51,16 +51,16 @@
</div>
</div>
<!-- Filter 2: Jahrgangsstufe -->
<div class="col-md-6">
<!-- Filter 2 -->
<div class="col-md-4">
<div class="card mb-4">
<div class="card-header">
<h2 class="card-title h5 mb-0">Jahrgangsstufe (Filter 2)</h2>
<h2 class="card-title h5 mb-0">System/Bereich (Filter 2)</h2>
</div>
<div class="card-body">
<form method="POST" action="{{ url_for('add_filter_value', filter_num=2) }}" class="mb-4">
<div class="input-group">
<input type="text" name="value" class="form-control" placeholder="Neue Jahrgangsstufe..." required>
<input type="text" name="value" class="form-control" placeholder="Neuer Wert..." required>
<button type="submit" class="btn btn-primary">Hinzufügen</button>
</div>
</form>
@@ -86,6 +86,42 @@
</div>
</div>
</div>
<!-- Filter 3 -->
<div class="col-md-4">
<div class="card mb-4">
<div class="card-header">
<h2 class="card-title h5 mb-0">Typ/Art (Filter 3)</h2>
</div>
<div class="card-body">
<form method="POST" action="{{ url_for('add_filter_value', filter_num=3) }}" class="mb-4">
<div class="input-group">
<input type="text" name="value" class="form-control" placeholder="Neuer Wert..." required>
<button type="submit" class="btn btn-primary">Hinzufügen</button>
</div>
</form>
<h5 class="mb-3">Vorhandene Werte</h5>
{% if filter3_values %}
<div class="list-group">
{% for value in filter3_values %}
<div class="list-group-item d-flex justify-content-between align-items-center">
{{ value }}
<form method="POST" action="{{ url_for('remove_filter_value', filter_num=3, value=value) }}" class="d-inline">
<button type="submit" class="btn btn-sm btn-danger"
onclick="return confirm('Sind Sie sicher, dass Sie den Wert \"' + '{{ value }}'.replace(/'/g, '\\\'') + '\" löschen möchten?');">
Entfernen
</button>
</form>
</div>
{% endfor %}
</div>
{% else %}
<div class="alert alert-info">Keine Werte definiert.</div>
{% endif %}
</div>
</div>
</div>
</div>
<div class="alert alert-warning">
-1
View File
@@ -709,7 +709,6 @@
</style>
<div class="upload-container">
<a href="{{ url_for(back_target|default('home_admin')) }}" class="nav-back-button">← Zurück zur Artikelübersicht</a>
{% if show_library_features %}
<div style="border:1px solid #dbe4ee; border-radius:8px; padding:14px; margin-bottom:16px; background:#f8fbff;">
+1 -7
View File
@@ -498,8 +498,6 @@ def check_nm_pwd(username, password):
finally:
client.close()
return None
def add_user(
username,
@@ -536,16 +534,12 @@ def add_user(
for key, value in page_permissions.items():
permission_defaults['pages'][str(key)] = bool(value)
alias_first = name if str(name or '').strip() else username
alias_last = last_name if str(last_name or '').strip() else ''
name_alias = build_name_synonym(alias_first, alias_last)
user_doc = {
'Username': username,
'Password': hashing(password),
'Admin': False,
'active_ausleihung': None,
'name': name_alias,
'name': name.strip() if name else '',
'last_name': last_name.strip() if last_name else '',
'IsStudent': bool(is_student),
'PermissionPreset': permission_defaults['preset'],