diff --git a/app/__pycache__/app.cpython-39.pyc b/app/__pycache__/app.cpython-39.pyc index 48dca6f..03f42ee 100644 Binary files a/app/__pycache__/app.cpython-39.pyc and b/app/__pycache__/app.cpython-39.pyc differ diff --git a/app/app.py b/app/app.py index 12ee52e..7510aac 100644 --- a/app/app.py +++ b/app/app.py @@ -2,6 +2,7 @@ from flask import Flask, redirect, url_for, render_template from views.index_views import index from views.user_views import user from views.group_views import group +from views.stats_views import stats from config import app_config @@ -24,6 +25,7 @@ app.logger.setLevel(logging.INFO) app.register_blueprint(index) app.register_blueprint(user, url_prefix='/user') app.register_blueprint(group, url_prefix='/group') +app.register_blueprint(stats, url_prefix='/stats') @app.route('/user_list') def legacy_user_list(): @@ -33,10 +35,6 @@ def legacy_user_list(): def legacy_group_list(): return redirect(url_for('group.group_list')) -@app.route('/stats') -def stats(): - return render_template('stats.html') - @app.route('/') def index_redirect(): return render_template('index.html') diff --git a/app/db_connection.py b/app/db_connection.py new file mode 100644 index 0000000..5742f2c --- /dev/null +++ b/app/db_connection.py @@ -0,0 +1,11 @@ +import mysql.connector +import os + +def get_connection(): + return mysql.connector.connect( + host=os.getenv('DB_HOST'), + port=int(os.getenv('DB_PORT', 3306)), + user=os.getenv('DB_USER'), + password=os.getenv('DB_PASSWORD'), + database=os.getenv('DB_NAME'), + ) diff --git a/app/db_interface.py b/app/db_interface.py index b2a5eee..7af9983 100644 --- a/app/db_interface.py +++ b/app/db_interface.py @@ -4,14 +4,8 @@ import datetime import requests import time import os - -def get_connection(): - return mysql.connector.connect( - host=current_app.config['DB_HOST'], - user=current_app.config['DB_USER'], - password=current_app.config['DB_PASSWORD'], - database=current_app.config['DB_NAME'] - ) +import pytz +from db_connection import get_connection def get_all_users(): @@ -45,10 +39,10 @@ def get_all_groups(): FROM groups g ORDER BY g.vlan_id """) - groups = cursor.fetchall() + available_groups = cursor.fetchall() cursor.close() conn.close() - return groups + return available_groups @@ -106,6 +100,7 @@ def duplicate_group(vlan_id): def add_user(mac_address, description, vlan_id): + print(f"→ Adding to DB: mac={mac_address}, desc={description}, vlan={vlan_id}") conn = get_connection() cursor = conn.cursor() cursor.execute( @@ -144,19 +139,61 @@ def delete_user(mac_address): conn.close() -def get_latest_auth_logs(result, limit=10): +def get_latest_auth_logs(reply_type=None, limit=5, time_range=None): conn = get_connection() cursor = conn.cursor(dictionary=True) - cursor.execute( - "SELECT * FROM auth_logs WHERE result = %s ORDER BY timestamp DESC LIMIT %s", - (result, limit) - ) + + # Determine the time filter based on the time_range + if time_range: + now = datetime.now(pytz.timezone(current_app.config.get('APP_TIMEZONE', 'UTC'))) + if time_range == 'last_minute': + time_filter = now - timedelta(minutes=1) + elif time_range == 'last_5_minutes': + time_filter = now - timedelta(minutes=5) + elif time_range == 'last_10_minutes': + time_filter = now - timedelta(minutes=10) + elif time_range == 'last_hour': + time_filter = now - timedelta(hours=1) + elif time_range == 'last_6_hours': + time_filter = now - timedelta(hours=6) + elif time_range == 'last_12_hours': + time_filter = now - timedelta(hours=12) + elif time_range == 'last_day': + time_filter = now - timedelta(days=1) + elif time_range == 'last_30_days': + time_filter = now - timedelta(days=30) + else: # 'all' case + time_filter = None + + if time_filter: + cursor.execute(""" + SELECT * FROM auth_logs + WHERE reply = %s AND timestamp >= %s + ORDER BY timestamp DESC + LIMIT %s + """, (reply_type, time_filter, limit)) + else: + cursor.execute(""" + SELECT * FROM auth_logs + WHERE reply = %s + ORDER BY timestamp DESC + LIMIT %s + """, (reply_type, limit)) + else: + cursor.execute(""" + SELECT * FROM auth_logs + WHERE reply = %s + ORDER BY timestamp DESC + LIMIT %s + """, (reply_type, limit)) + logs = cursor.fetchall() cursor.close() conn.close() return logs + def get_vendor_info(mac, insert_if_found=True): conn = get_connection() cursor = conn.cursor(dictionary=True) @@ -410,3 +447,14 @@ def lookup_mac_verbose(mac): conn.close() return "\n".join(output) +def get_user_by_mac(mac_address): + conn = get_connection() + cursor = conn.cursor(dictionary=True) + + cursor.execute(""" + SELECT * FROM users WHERE mac_address = %s + """, (mac_address,)) + user = cursor.fetchone() + cursor.close() + conn.close() + return user diff --git a/app/static/styles.css b/app/static/styles.css index 2184883..39820b3 100644 --- a/app/static/styles.css +++ b/app/static/styles.css @@ -115,7 +115,12 @@ table.styled-table { .styled-table th { background-color: var(--header-bg); color: var(--fg); - text-align: left; + text-align: center; +} + +/* 🧩 Fix: Remove right border from last column */ +.styled-table thead th:last-child { + border-right: none; } .styled-table input[type="text"], @@ -235,4 +240,57 @@ form.inline-form { font-size: 0.9rem; white-space: pre-wrap; margin-top: 1em; -} \ No newline at end of file +} + +.stats-page .stats-container { + display: flex; + flex-wrap: wrap; + gap: 2rem; +} + +.stats-page .card { + flex: 1; + min-width: 45%; + padding: 1rem; + border-radius: 8px; + background-color: var(--card-bg); + color: var(--fg); + box-shadow: 0 0 10px rgba(0,0,0,0.2); +} + +.stats-page .success-card { + border-left: 6px solid limegreen; +} + +.stats-page .error-card { + border-left: 6px solid crimson; +} + +.stats-page .fallback-card { + border-left: 6px solid orange; +} + +.stats-page .styled-table.small-table td, +.stats-page .styled-table.small-table th { + padding: 6px; + font-size: 0.9rem; +} + +.stats-page form.inline-form { + display: flex; + align-items: center; + gap: 6px; + flex-wrap: nowrap; + white-space: nowrap; +} + +.stats-page form.inline-form select { + flex: 1; + min-width: 140px; + max-width: 100%; +} + +.stats-page form.inline-form button { + flex: 0 0 auto; + padding: 6px; +} diff --git a/app/templates/base.html b/app/templates/base.html index e058767..c28eebb 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -12,7 +12,7 @@ Home Users Groups - Stats + Stats
diff --git a/app/templates/group_list.html b/app/templates/group_list.html index 0af8c34..3835df2 100644 --- a/app/templates/group_list.html +++ b/app/templates/group_list.html @@ -20,7 +20,7 @@ - {% for group in groups %} + {% for group in available_groups %} {{ group.vlan_id }} diff --git a/app/templates/index.html b/app/templates/index.html index df15bbe..cc02df1 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -15,30 +15,27 @@
-

Recent Access-Accept

-