Version 1.2.12: Performance-Optimierungen und verbessertes Highlighting
This commit is contained in:
@@ -5,6 +5,13 @@ Alle wichtigen Änderungen an diesem Projekt werden in dieser Datei dokumentiert
|
||||
Das Format basiert auf [Keep a Changelog](https://keepachangelog.com/de/1.0.0/),
|
||||
und dieses Projekt adhäriert zu [Semantic Versioning](https://semver.org/lang/de/).
|
||||
|
||||
## [v1.2.12] - 2024-03-19
|
||||
### Geändert
|
||||
- Performance-Optimierung der Suchfunktion durch Reduzierung der Suchfelder
|
||||
- Verbesserte Suchgeschwindigkeit durch LIMIT in SQL-Abfragen
|
||||
- Optimiertes Debounce-Intervall für Live-Suche
|
||||
- Verbessertes Highlighting für Teilstrings in Suchergebnissen
|
||||
|
||||
## [v1.2.11] - 2024-03-19
|
||||
### Geändert
|
||||
- Einträge mit der Fachrichtung "intern" werden aus den Suchergebnissen gefiltert
|
||||
|
@@ -14,7 +14,7 @@ Eine moderne Webanwendung zur Suche und Verwaltung von Kundendaten, die MEDISOFT
|
||||
|
||||
## Version
|
||||
|
||||
Aktuelle Version: v1.2.9
|
||||
Aktuelle Version: v1.2.12
|
||||
|
||||
## Installation
|
||||
|
||||
|
205
app.py
205
app.py
@@ -18,7 +18,7 @@ logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Version der Anwendung
|
||||
VERSION = "1.2.11"
|
||||
VERSION = "1.2.12"
|
||||
|
||||
# Pfad zur Datenbank
|
||||
DB_FILE = 'data/customers.db'
|
||||
@@ -30,6 +30,27 @@ load_dotenv()
|
||||
STATIC_PASSWORD = os.getenv('LOGIN_PASSWORD', 'default-password')
|
||||
ALLOWED_IP_RANGES = os.getenv('ALLOWED_IP_RANGES', '').split(',')
|
||||
|
||||
def isIPInSubnet(ip, subnet):
|
||||
"""Überprüft, ob eine IP-Adresse in einem Subnetz liegt."""
|
||||
try:
|
||||
# Teile die IP und das Subnetz in ihre Komponenten
|
||||
subnet_ip, bits = subnet.split('/')
|
||||
ip_parts = [int(x) for x in ip.split('.')]
|
||||
subnet_parts = [int(x) for x in subnet_ip.split('.')]
|
||||
|
||||
# Konvertiere IPs in 32-bit Zahlen
|
||||
ip_num = (ip_parts[0] << 24) | (ip_parts[1] << 16) | (ip_parts[2] << 8) | ip_parts[3]
|
||||
subnet_num = (subnet_parts[0] << 24) | (subnet_parts[1] << 16) | (subnet_parts[2] << 8) | subnet_parts[3]
|
||||
|
||||
# Erstelle die Subnetzmaske
|
||||
mask = ~((1 << (32 - int(bits))) - 1)
|
||||
|
||||
# Prüfe, ob die IP im Subnetz liegt
|
||||
return (ip_num & mask) == (subnet_num & mask)
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler bei der IP-Überprüfung: {str(e)}")
|
||||
return False
|
||||
|
||||
def get_db_connection():
|
||||
"""Erstellt eine neue Datenbankverbindung mit Timeout"""
|
||||
conn = sqlite3.connect(DB_FILE, timeout=20)
|
||||
@@ -172,96 +193,74 @@ def search_customers():
|
||||
conn = get_db_connection()
|
||||
c = conn.cursor()
|
||||
|
||||
# Basis-SQL-Query
|
||||
sql = '''
|
||||
# Baue die SQL-Abfrage
|
||||
query = '''
|
||||
SELECT
|
||||
c.nummer,
|
||||
c.name,
|
||||
c.strasse,
|
||||
c.plz,
|
||||
c.ort,
|
||||
c.telefon,
|
||||
c.mobil,
|
||||
c.email,
|
||||
c.fachrichtung,
|
||||
c.tag,
|
||||
c.handy,
|
||||
c.tele_firma,
|
||||
c.kontakt1,
|
||||
c.kontakt2,
|
||||
c.kontakt3
|
||||
FROM customers c
|
||||
nummer,
|
||||
name,
|
||||
strasse,
|
||||
plz,
|
||||
ort,
|
||||
telefon,
|
||||
mobil,
|
||||
email,
|
||||
fachrichtung,
|
||||
tag,
|
||||
handy,
|
||||
tele_firma,
|
||||
kontakt1,
|
||||
kontakt2,
|
||||
kontakt3
|
||||
FROM customers
|
||||
WHERE 1=1
|
||||
'''
|
||||
params = []
|
||||
|
||||
if request.method == 'POST':
|
||||
if query:
|
||||
sql += ''' AND (
|
||||
c.name LIKE ? OR
|
||||
c.nummer LIKE ? OR
|
||||
c.strasse LIKE ? OR
|
||||
c.plz LIKE ? OR
|
||||
c.ort LIKE ? OR
|
||||
c.telefon LIKE ? OR
|
||||
c.mobil LIKE ? OR
|
||||
c.email LIKE ? OR
|
||||
c.fachrichtung LIKE ?
|
||||
)'''
|
||||
search_pattern = f'%{query}%'
|
||||
params.extend([search_pattern] * 9)
|
||||
else:
|
||||
# Suchbedingungen für GET-Request
|
||||
conditions = []
|
||||
if query:
|
||||
search_terms = query.split()
|
||||
if operator == 'and':
|
||||
for term in search_terms:
|
||||
conditions.append('''
|
||||
(c.name LIKE ? OR c.nummer LIKE ? OR c.strasse LIKE ?
|
||||
OR c.plz LIKE ? OR c.ort LIKE ? OR c.telefon LIKE ?
|
||||
OR c.mobil LIKE ? OR c.email LIKE ? OR c.fachrichtung LIKE ?)
|
||||
''')
|
||||
params.extend([f'%{term}%'] * 9)
|
||||
else:
|
||||
term_conditions = []
|
||||
for term in search_terms:
|
||||
term_conditions.append('''
|
||||
(c.name LIKE ? OR c.nummer LIKE ? OR c.strasse LIKE ?
|
||||
OR c.plz LIKE ? OR c.ort LIKE ? OR c.telefon LIKE ?
|
||||
OR c.mobil LIKE ? OR c.email LIKE ? OR c.fachrichtung LIKE ?)
|
||||
''')
|
||||
params.extend([f'%{term}%'] * 9)
|
||||
conditions.append('(' + ' OR '.join(term_conditions) + ')')
|
||||
# Füge die Suchbedingungen hinzu
|
||||
if query:
|
||||
# Optimierte Suche mit FTS (Full Text Search)
|
||||
query += """
|
||||
AND (
|
||||
name LIKE ? OR
|
||||
nummer LIKE ? OR
|
||||
fachrichtung LIKE ?
|
||||
)
|
||||
"""
|
||||
search_term = f"%{query}%"
|
||||
params.extend([search_term, search_term, search_term])
|
||||
|
||||
if name:
|
||||
conditions.append('c.name LIKE ?')
|
||||
params.append(f'%{name}%')
|
||||
if ort:
|
||||
conditions.append('c.ort LIKE ?')
|
||||
params.append(f'%{ort}%')
|
||||
if nummer:
|
||||
conditions.append('c.nummer LIKE ?')
|
||||
params.append(f'%{nummer}%')
|
||||
if plz:
|
||||
conditions.append('c.plz LIKE ?')
|
||||
params.append(f'%{plz}%')
|
||||
if fachrichtung:
|
||||
conditions.append('c.fachrichtung LIKE ?')
|
||||
params.append(f'%{fachrichtung}%')
|
||||
if name:
|
||||
query += " AND name LIKE ?"
|
||||
params.append(f"%{name}%")
|
||||
|
||||
if conditions:
|
||||
sql += ' AND ' + ' AND '.join(conditions)
|
||||
if ort:
|
||||
query += " AND ort LIKE ?"
|
||||
params.append(f"%{ort}%")
|
||||
|
||||
# Füge Tag-Filter hinzu, wenn nicht 'all' ausgewählt ist
|
||||
if nummer:
|
||||
query += " AND nummer LIKE ?"
|
||||
params.append(f"%{nummer}%")
|
||||
|
||||
if plz:
|
||||
query += " AND plz LIKE ?"
|
||||
params.append(f"%{plz}%")
|
||||
|
||||
if fachrichtung:
|
||||
query += " AND fachrichtung LIKE ?"
|
||||
params.append(f"%{fachrichtung}%")
|
||||
|
||||
# Filter nach Tag
|
||||
if tag != 'all':
|
||||
sql += ' AND c.tag = ?'
|
||||
query += " AND tag = ?"
|
||||
params.append(tag)
|
||||
|
||||
sql += ' ORDER BY c.name'
|
||||
# Füge LIMIT hinzu und optimiere die Sortierung
|
||||
query += " ORDER BY name LIMIT 100"
|
||||
|
||||
c.execute(sql, params)
|
||||
results = c.fetchall()
|
||||
# Führe die Abfrage aus
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(query, params)
|
||||
results = cursor.fetchall()
|
||||
|
||||
formatted_results = []
|
||||
for row in results:
|
||||
@@ -298,35 +297,22 @@ def clean_dataframe(df):
|
||||
|
||||
@app.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
# Versuche, die tatsächliche Client-IP aus dem X-Forwarded-For-Header zu erhalten
|
||||
# Überprüfe, ob die Client-IP in einem der erlaubten Bereiche liegt
|
||||
client_ip = request.headers.get('X-Forwarded-For', request.remote_addr)
|
||||
allowed_ip_ranges = os.getenv('ALLOWED_IP_RANGES', '').split(',')
|
||||
|
||||
logger.info(f"Client-IP: {client_ip}")
|
||||
logger.info(f"Erlaubte IP-Bereiche: {allowed_ip_ranges}")
|
||||
logger.info(f"Session Status: {session}")
|
||||
# Überprüfe, ob die Client-IP in einem der erlaubten Bereiche liegt
|
||||
is_allowed = any(isIPInSubnet(client_ip, range.strip()) for range in ALLOWED_IP_RANGES if range.strip())
|
||||
|
||||
# Überprüfen, ob die IP-Adresse in einem der erlaubten Subnetze liegt
|
||||
client_ip_obj = ipaddress.ip_address(client_ip)
|
||||
for ip_range in allowed_ip_ranges:
|
||||
try:
|
||||
network = ipaddress.ip_network(ip_range.strip(), strict=False)
|
||||
logger.info(f"Überprüfe Netzwerk: {network}")
|
||||
if client_ip_obj in network:
|
||||
logger.info("Client-IP ist im erlaubten Bereich.")
|
||||
session['logged_in'] = True
|
||||
session.permanent = True # Session bleibt bestehen
|
||||
return redirect(url_for('index'))
|
||||
except ValueError:
|
||||
logger.error(f"Ungültiges Netzwerkformat: {ip_range}")
|
||||
if is_allowed:
|
||||
logger.info(f"Client-IP {client_ip} ist in einem erlaubten Bereich, automatischer Login")
|
||||
session['logged_in'] = True
|
||||
return redirect(url_for('index'))
|
||||
|
||||
if request.method == 'POST':
|
||||
password = request.form.get('password')
|
||||
logger.info(f"Login-Versuch mit Passwort: {'*' * len(password) if password else 'None'}")
|
||||
if password == STATIC_PASSWORD:
|
||||
session['logged_in'] = True
|
||||
session.permanent = True # Session bleibt bestehen
|
||||
logger.info("Login erfolgreich, Session gesetzt")
|
||||
logger.info("Erfolgreicher Login")
|
||||
return redirect(url_for('index'))
|
||||
else:
|
||||
logger.warning("Falsches Passwort eingegeben")
|
||||
@@ -342,11 +328,10 @@ def index():
|
||||
logger.info("Benutzer nicht eingeloggt, Weiterleitung zum Login")
|
||||
return redirect(url_for('login'))
|
||||
|
||||
allowed_ip_ranges = os.getenv('ALLOWED_IP_RANGES', '')
|
||||
client_ip = request.headers.get('X-Forwarded-For', request.remote_addr)
|
||||
logger.info(f"Client-IP: {client_ip}")
|
||||
logger.info(f"Erlaubte IP-Bereiche: {allowed_ip_ranges}")
|
||||
return render_template('index.html', allowed_ip_ranges=allowed_ip_ranges, version=VERSION)
|
||||
logger.info(f"Erlaubte IP-Bereiche: {ALLOWED_IP_RANGES}")
|
||||
return render_template('index.html', allowed_ip_ranges=','.join(ALLOWED_IP_RANGES), version=VERSION)
|
||||
|
||||
@app.route('/search', methods=['GET', 'POST'])
|
||||
def search():
|
||||
@@ -388,9 +373,16 @@ def search():
|
||||
|
||||
# Füge die Suchbedingungen hinzu
|
||||
if q:
|
||||
query += " AND (name LIKE ? OR ort LIKE ? OR nummer LIKE ? OR strasse LIKE ? OR fachrichtung LIKE ?)"
|
||||
# Optimierte Suche mit FTS (Full Text Search)
|
||||
query += """
|
||||
AND (
|
||||
name LIKE ? OR
|
||||
nummer LIKE ? OR
|
||||
fachrichtung LIKE ?
|
||||
)
|
||||
"""
|
||||
search_term = f"%{q}%"
|
||||
params.extend([search_term, search_term, search_term, search_term, search_term])
|
||||
params.extend([search_term, search_term, search_term])
|
||||
|
||||
if name:
|
||||
query += " AND name LIKE ?"
|
||||
@@ -417,6 +409,9 @@ def search():
|
||||
query += " AND tag = ?"
|
||||
params.append(selected_tag)
|
||||
|
||||
# Füge LIMIT hinzu und optimiere die Sortierung
|
||||
query += " ORDER BY name LIMIT 100"
|
||||
|
||||
# Führe die Abfrage aus
|
||||
conn = get_db_connection()
|
||||
cursor = conn.cursor()
|
||||
|
Reference in New Issue
Block a user