re-worked the maintenance interface, added more stats
This commit is contained in:
@@ -637,6 +637,33 @@ def get_summary_counts():
|
|||||||
|
|
||||||
return total_users, total_groups
|
return total_users, total_groups
|
||||||
|
|
||||||
|
def get_database_stats():
|
||||||
|
conn = get_connection()
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
stats = {}
|
||||||
|
|
||||||
|
# Get total size of the database
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT table_schema AS db_name,
|
||||||
|
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS total_mb
|
||||||
|
FROM information_schema.tables
|
||||||
|
WHERE table_schema = DATABASE()
|
||||||
|
GROUP BY table_schema
|
||||||
|
""")
|
||||||
|
row = cursor.fetchone()
|
||||||
|
stats["total_size_mb"] = row[1] if row else 0
|
||||||
|
|
||||||
|
# Optional: count total rows in key tables
|
||||||
|
cursor.execute("SELECT COUNT(*) FROM auth_logs")
|
||||||
|
stats["auth_logs_count"] = cursor.fetchone()[0]
|
||||||
|
|
||||||
|
cursor.execute("SELECT COUNT(*) FROM users")
|
||||||
|
stats["users_count"] = cursor.fetchone()[0]
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
return stats
|
||||||
|
|
||||||
# ------------------------------
|
# ------------------------------
|
||||||
# Maintenance Functions
|
# Maintenance Functions
|
||||||
# ------------------------------
|
# ------------------------------
|
||||||
@@ -725,4 +752,6 @@ def get_table_stats():
|
|||||||
return None
|
return None
|
||||||
finally:
|
finally:
|
||||||
cursor.close()
|
cursor.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,67 +1,93 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% block title %}Maintenance{% endblock %}
|
{% block title %}Maintenance{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>Database Maintenance</h1>
|
<div class="maintenance-page">
|
||||||
|
<h1>Database Maintenance</h1>
|
||||||
|
|
||||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
<div class="flash-messages">
|
<div class="flash-messages">
|
||||||
{% for category, message in messages %}
|
{% for category, message in messages %}
|
||||||
<div class="alert alert-{{ category }}">{{ message }}</div>
|
<div class="alert alert-{{ category }}">{{ message }}</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endwith %}
|
|
||||||
|
|
||||||
<p>Perform common database maintenance tasks here.</p>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<h2>Database Statistics</h2>
|
|
||||||
<div class="database-stats">
|
|
||||||
{% if table_stats %}
|
|
||||||
{% for table, row_count in table_stats.items() %}
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">{{ table }}</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<p>Number of rows: {{ row_count }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% else %}
|
|
||||||
<p>Could not retrieve database statistics.</p>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
<hr>
|
<div class="section">
|
||||||
|
<div class="card neutral">
|
||||||
|
<div class="card-header">Database Overview</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<table class="styled-table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>Database Size</th>
|
||||||
|
<td>{{ db_stats.total_size_mb }} MB</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>auth_logs Rows</th>
|
||||||
|
<td>{{ db_stats.auth_logs_count }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>users Rows</th>
|
||||||
|
<td>{{ db_stats.users_count }}</td>
|
||||||
|
</tr>
|
||||||
|
{% if table_stats %}
|
||||||
|
{% for table, row_count in table_stats.items() %}
|
||||||
|
{% if table != 'auth_logs' and table != 'users' %}
|
||||||
|
<tr>
|
||||||
|
<th>{{ table }} Rows</th>
|
||||||
|
<td>{{ row_count }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h2>Clear Authentication Logs</h2>
|
<div class="section">
|
||||||
<p>Permanently remove all authentication logs from the database. This action cannot be undone.</p>
|
<div class="card">
|
||||||
<form action="/maintenance/clear_auth_logs" method="post">
|
<div class="card-header">Clear auth_logs Table</div>
|
||||||
<button type="submit" class="btn btn-danger" onclick="return confirm('Are you sure you want to clear all authentication logs? This action is irreversible!')">
|
<div class="card-body">
|
||||||
|
<p>Permanently remove all rows from the <code>auth_logs</code> table. This action cannot be undone.</p>
|
||||||
|
<form action="/maintenance/clear_auth_logs" method="post">
|
||||||
|
<button type="submit" class="btn btn-danger" onclick="return confirm('Are you sure you want to clear all authentication logs? This action is irreversible!')">
|
||||||
Clear Logs
|
Clear Logs
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<hr>
|
<div class="section">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">Backup Database</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p>Dump the current SQL database to a downloadable file.</p>
|
||||||
|
<p class="alert-error" style="margin: 1rem 0;">Warning: Backup size can be large if <code>auth_logs</code> has not been cleared.</p>
|
||||||
|
<form action="/maintenance/backup_database" method="get">
|
||||||
|
<button type="submit" class="btn">Backup Database</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h2>Database Backup</h2>
|
<div class="section">
|
||||||
<p>Create a backup of the current database. The backup will be saved as a SQL file.</p>
|
<div class="card">
|
||||||
<p style="color: red;">
|
<div class="card-header">Restore Database</div>
|
||||||
Warning: Database backups can be very large if you do not clear the authentication logs first.
|
<div class="card-body">
|
||||||
</p>
|
<p>Restore the SQL database from a previously exported file. This will overwrite all current data.</p>
|
||||||
<form action="/maintenance/backup_database" method="get">
|
<form action="/maintenance/restore_database" method="post" enctype="multipart/form-data">
|
||||||
<button type="submit" class="btn btn-primary">Backup Database</button>
|
<input type="file" name="file" accept=".sql" required>
|
||||||
</form>
|
<button type="submit" class="btn btn-danger" onclick="return confirm('Are you sure you want to restore the database from this file? This will OVERWRITE the current database.')">
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<h2>Database Restore</h2>
|
|
||||||
<p>Restore the database from a previously created SQL backup file. This will overwrite the current database.</p>
|
|
||||||
<form action="/maintenance/restore_database" method="post" enctype="multipart/form-data">
|
|
||||||
<input type="file" name="file" accept=".sql" required>
|
|
||||||
<button type="submit" class="btn btn-danger" onclick="return confirm('Are you sure you want to restore the database from this file? This will OVERWRITE the current database.')">
|
|
||||||
Restore Database
|
Restore Database
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
from flask import Blueprint, render_template, request, send_file
|
from flask import Blueprint, render_template, request, send_file
|
||||||
import mysql.connector
|
import mysql.connector
|
||||||
import os
|
import os
|
||||||
from db_interface import clear_auth_logs, backup_database, restore_database, get_table_stats # Import the functions from db_interface.py
|
from db_interface import get_database_stats, clear_auth_logs, backup_database, restore_database, get_table_stats # Import the functions from db_interface.py
|
||||||
|
|
||||||
|
|
||||||
maintenance = Blueprint('maintenance', __name__, url_prefix='/maintenance')
|
maintenance = Blueprint('maintenance', __name__, url_prefix='/maintenance')
|
||||||
|
|
||||||
@maintenance.route('/')
|
@maintenance.route('/')
|
||||||
def maintenance_page():
|
def maintenance_page():
|
||||||
"""Renders the maintenance page."""
|
"""Renders the maintenance page with table and DB stats."""
|
||||||
table_stats = get_table_stats()
|
table_stats = get_table_stats()
|
||||||
return render_template('maintenance.html', table_stats=table_stats)
|
db_stats = get_database_stats()
|
||||||
|
return render_template('maintenance.html', table_stats=table_stats, db_stats=db_stats)
|
||||||
|
|
||||||
@maintenance.route('/clear_auth_logs', methods=['POST'])
|
@maintenance.route('/clear_auth_logs', methods=['POST'])
|
||||||
def clear_auth_logs_route():
|
def clear_auth_logs_route():
|
||||||
@@ -47,4 +48,4 @@ def restore_database_route():
|
|||||||
message = restore_database(sql_content)
|
message = restore_database(sql_content)
|
||||||
return message
|
return message
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return str(e), 500
|
return str(e), 500
|
||||||
Reference in New Issue
Block a user