337 lines
16 KiB
HTML
337 lines
16 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
{% load i18n %}
|
|
|
|
{% block title %}{% trans "Audit Dashboard" %}{% endblock %}
|
|
|
|
{% block customCSS %}
|
|
<style>
|
|
/* ---------------------------------------------------- */
|
|
/* 1. Theme Variables (Teal Focus) */
|
|
/* ---------------------------------------------------- */
|
|
:root {
|
|
--color-primary: #007a88; /* Main Teal */
|
|
--color-primary-dark: #004d55; /* Dark Teal for Headings, Login Status, and Strong Text */
|
|
|
|
/* Standard Dark Text (for max visibility) */
|
|
/* Muted text */
|
|
--color-text-on-dark: #f0f0f0; /* Light text for badges (guaranteed contrast) */
|
|
|
|
/* Adjusted Status Colors for contrast */
|
|
--color-success: #157347;
|
|
--color-danger: #bb2d3b;
|
|
--color-warning: #ffc107;
|
|
--color-info: #0dcaf0;
|
|
--color-light: #f8f9fa;
|
|
|
|
}
|
|
|
|
/* ---------------------------------------------------- */
|
|
/* 2. Layout, Header, and Summary */
|
|
/* ---------------------------------------------------- */
|
|
.container-fluid {
|
|
background-color: var(--color-background-light);
|
|
}
|
|
.audit-card {
|
|
background-color: #ffffff;
|
|
border-radius: 0.75rem;
|
|
border: 1px solid #e9ecef;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
|
min-height: 60vh;
|
|
}
|
|
.dashboard-header {
|
|
color: var(--color-primary-dark);
|
|
border-bottom: 1px solid #e9ecef;
|
|
padding-bottom: 1rem;
|
|
}
|
|
.summary-alert {
|
|
border-color: var(--color-primary) !important;
|
|
background-color: var(--color-primary-light) !important;
|
|
}
|
|
.summary-alert h6, .summary-alert strong {
|
|
color: var(--color-primary-dark) !important;
|
|
}
|
|
|
|
/* ---------------------------------------------------- */
|
|
/* 3. Tabs Styling */
|
|
/* ---------------------------------------------------- */
|
|
.nav-tabs {
|
|
border-bottom: 2px solid #e9ecef;
|
|
background-color: #ffffff;
|
|
padding-top: 1rem;
|
|
border-radius: 0.75rem 0.75rem 0 0;
|
|
}
|
|
.nav-link-es {
|
|
color: var(--color-text-secondary);
|
|
border: none;
|
|
}
|
|
.nav-link-es.active {
|
|
color: var(--color-primary) !important;
|
|
font-weight: 600;
|
|
border-bottom: 3px solid var(--color-primary) !important;
|
|
}
|
|
.nav-link:hover:not(.active) {
|
|
color: var(--color-primary);
|
|
}
|
|
|
|
/* ---------------------------------------------------- */
|
|
/* 4. Table and Text Contrast */
|
|
/* ---------------------------------------------------- */
|
|
.table th {
|
|
color: var(--color-text-secondary);
|
|
}
|
|
.table td {
|
|
color: var(--color-text-dark);
|
|
}
|
|
|
|
pre {
|
|
background-color: var(--color-light) !important;
|
|
color: var(--color-text-dark) !important;
|
|
}
|
|
code {
|
|
color: var(--color-text-dark) !important;
|
|
}
|
|
|
|
/* ---------------------------------------------------- */
|
|
/* 5. badges VISIBILITY FIXES (Guaranteed Colors) */
|
|
/* ---------------------------------------------------- */
|
|
.badges {
|
|
font-weight: 600;
|
|
line-height: 1.4;
|
|
padding: 0.4em 0.6em;
|
|
min-width: 65px;
|
|
text-align: center;
|
|
text-transform: uppercase;
|
|
/* Ensure z-index doesn't cause issues if elements overlap */
|
|
position: relative;
|
|
}
|
|
|
|
/* Dark badgess (CRUD Create/Delete & Login/Logout Type) - Use light text */
|
|
.badges-crud-create {
|
|
background-color: var(--color-success) !important;
|
|
color: var(--color-text-on-dark) !important;
|
|
}
|
|
.badges-crud-delete {
|
|
background-color: var(--color-danger) !important;
|
|
color: var(--color-text-on-dark) !important;
|
|
}
|
|
.badges-login-status {
|
|
background-color: var(--color-primary-dark) !important;
|
|
color: var(--color-text-on-dark) !important;
|
|
}
|
|
|
|
/* Light badgess (CRUD Update & Request Method) - Use dark text */
|
|
.badges-crud-update {
|
|
background-color: var(--color-warning) !important;
|
|
color: var(--color-text-dark) !important;
|
|
}
|
|
.badges-request-method {
|
|
background-color: var(--color-info) !important;
|
|
color: var(--color-text-dark) !important;
|
|
}
|
|
|
|
|
|
/* Pagination - Fully Teal Themed */
|
|
.pagination .page-item.active .page-link {
|
|
background-color: var(--color-primary) !important;
|
|
border-color: var(--color-primary) !important;
|
|
color: var(--color-text-on-dark) !important;
|
|
}
|
|
.pagination .page-link {
|
|
color: var(--color-primary) !important; /* FIX: Added !important here */
|
|
border: 1px solid #dee2e6;
|
|
}
|
|
.pagination .page-item.disabled .page-link {
|
|
color: var(--color-text-secondary) !important;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid pt-5 pb-5 px-lg-5" style="background-color: var(--color-background-light);">
|
|
<h1 class="h3 fw-bold dashboard-header mb-4 px-3">
|
|
<i class="fas fa-shield-alt me-2" style="color: var(--color-primary);"></i>{% trans "System Activity Logs" %}
|
|
</h1>
|
|
|
|
<div class="alert summary-alert border-start border-5 p-3 mb-5 mx-3" role="alert">
|
|
<h6 class="mb-1">{% trans "Viewing Logs" %}: <strong>{{ tab_title }}</strong></h6>
|
|
<p class="mb-0 small">
|
|
{% trans "Displaying" %}: **{{ logs.start_index }}-{{ logs.end_index }}** {% trans "of" %}
|
|
**{{ total_count }}** {% trans "total records." %}
|
|
</p>
|
|
</div>
|
|
|
|
<div class="audit-card mx-3">
|
|
|
|
<ul class="nav nav-tabs px-3" id="auditTabs" role="tablist">
|
|
<li class="nav-item" role="presentation">
|
|
<a class="nav-link nav-link-es {% if active_tab == 'crud' %}active{% endif %}"
|
|
id="crud-tab" href="?tab=crud" aria-controls="crud">
|
|
<i class="fas fa-database me-2"></i>{% trans "Model Changes (CRUD)" %}
|
|
</a>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<a class="nav-link nav-link-es {% if active_tab == 'login' %}active{% endif %}"
|
|
id="login-tab" href="?tab=login" aria-controls="login">
|
|
<i class="fas fa-user-lock me-2"></i>{% trans "User Authentication" %}
|
|
</a>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<a class="nav-link nav-link-es {% if active_tab == 'request' %}active{% endif %}"
|
|
id="request-tab" href="?tab=request" aria-controls="request">
|
|
<i class="fas fa-globe me-2"></i>{% trans "HTTP Requests" %}
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<div class="tab-content p-4" id="auditTabsContent">
|
|
|
|
<div class="tab-pane fade show active" role="tabpanel">
|
|
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-hover small">
|
|
|
|
<thead>
|
|
{% if active_tab == 'crud' %}
|
|
<tr>
|
|
<th scope="col" style="width: 15%;">{% trans "Date/Time" %}</th>
|
|
<th scope="col" style="width: 15%;">{% trans "User" %}</th>
|
|
<th scope="col" style="width: 10%;">{% trans "Action" %}</th>
|
|
<th scope="col" style="width: 20%;">{% trans "Model" %}</th>
|
|
<th scope="col" style="width: 10%;">{% trans "Object PK" %}</th>
|
|
<th scope="col" style="width: 30%;">{% trans "Changes" %}</th>
|
|
</tr>
|
|
{% elif active_tab == 'login' %}
|
|
<tr>
|
|
<th scope="col" style="width: 20%;">{% trans "Date/Time" %}</th>
|
|
<th scope="col" style="width: 25%;">{% trans "User" %}</th>
|
|
<th scope="col" style="width: 15%;">{% trans "Type" %}</th>
|
|
<th scope="col" style="width: 10%;">{% trans "Status" %}</th>
|
|
<th scope="col" style="width: 30%;">{% trans "IP Address" %}</th>
|
|
</tr>
|
|
{% elif active_tab == 'request' %}
|
|
<tr>
|
|
<th scope="col" style="width: 15%;">{% trans "Date/Time" %}</th>
|
|
<th scope="col" style="width: 15%;">{% trans "User" %}</th>
|
|
<th scope="col" style="width: 10%;">{% trans "Method" %}</th>
|
|
<th scope="col" style="width: 45%;">{% trans "Path" %}</th>
|
|
|
|
</tr>
|
|
{% endif %}
|
|
</thead>
|
|
|
|
<tbody>
|
|
{% for log in logs.object_list %}
|
|
{% if active_tab == 'crud' %}
|
|
<tr>
|
|
<td>{{ log.datetime|date:"Y-m-d H:i:s" }}</td>
|
|
<td>{{ log.user.email|default:"N/A" }}</td>
|
|
<td>
|
|
<span class="badge rounded-pill
|
|
{% if log.event_type == 1 %}badges-crud-create
|
|
{% elif log.event_type == 2 %}badges-crud-update
|
|
{% else %}badges-crud-delete{% endif %}">
|
|
{% if log.event_type == 1 %}<i class="fas fa-plus-circle me-1"></i>{% trans "CREATE" %}
|
|
{% elif log.event_type == 2 %}<i class="fas fa-edit me-1"></i>{% trans "UPDATE" %}
|
|
{% else %}<i class="fas fa-trash-alt me-1"></i>{% trans "DELETE" %}{% endif %}
|
|
</span>
|
|
</td>
|
|
<td><code style="color: var(--color-text-dark) !important;">{{ log.content_type.app_label }}.{{ log.content_type.model }}</code></td>
|
|
<td>{{ log.object_id }}</td>
|
|
<td>
|
|
<pre class="p-2 m-0" style="font-size: 0.65rem; max-height: 80px; overflow-y: auto;">{{ log.changed_fields }}</pre>
|
|
</td>
|
|
</tr>
|
|
|
|
{% elif active_tab == 'login' %}
|
|
<tr>
|
|
<td>{{ log.datetime|date:"Y-m-d H:i:s" }}</td>
|
|
<td>
|
|
{% with user_obj=log.user %}
|
|
{% if user_obj %}
|
|
{{ user_obj.get_full_name|default:user_obj.username }}
|
|
{% else %}
|
|
<span class="text-danger fw-bold">{{ log.username|default:"N/A" }}</span>
|
|
{% endif %}
|
|
{% endwith %}
|
|
</td>
|
|
<td>
|
|
<span class="badge rounded-pill badges-login-status">
|
|
{% if log.login_type == 0 %}{% trans "Login" %}
|
|
{% elif log.login_type == 1 %}{% trans "Logout" %}
|
|
{% else %}{% trans "Failed Login" %}{% endif %}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
{% if log.login_type == 2 %}
|
|
<i class="fas fa-times-circle me-1" style="color: var(--color-danger);"></i>{% trans "Failed" %}
|
|
{% else %}
|
|
<i class="fas fa-check-circle me-1" style="color: var(--color-success);"></i>{% trans "Success" %}
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ log.remote_ip|default:"Unknown" }}</td>
|
|
</tr>
|
|
|
|
{% elif active_tab == 'request' %}
|
|
<tr>
|
|
<td>{{ log.datetime|date:"Y-m-d H:i:s" }}</td>
|
|
<td>{{ log.user.get_full_name|default:log.user.email|default:"Anonymous" }}</td>
|
|
<td>
|
|
<span class="badge rounded-pill badges-request-method">{{ log.method }}</span>
|
|
</td>
|
|
<td><code class="text-break small" style="color: var(--color-text-dark) !important;">{{ log.url}}</code></td>
|
|
|
|
</tr>
|
|
{% endif %}
|
|
{% empty %}
|
|
<tr><td colspan="6" class="text-center text-muted py-5">
|
|
<i class="fas fa-info-circle me-2"></i>{% trans "No logs found for this section or the database is empty." %}
|
|
</td></tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{% if logs.has_other_pages %}
|
|
<nav aria-label="Audit Log Pagination" class="pt-3">
|
|
<ul class="pagination justify-content-end">
|
|
|
|
<li class="page-item {% if not logs.has_previous %}disabled{% endif %}">
|
|
<a class="page-link"
|
|
href="?tab={{ active_tab }}{% if logs.has_previous %}&page={{ logs.previous_page_number }}{% endif %}"
|
|
aria-label="Previous">
|
|
<span aria-hidden="true">«</span>
|
|
</a>
|
|
</li>
|
|
|
|
{% for i in logs.paginator.page_range %}
|
|
{% comment %} Limiting pages displayed around the current page {% endcomment %}
|
|
{% if i > logs.number|add:'-3' and i < logs.number|add:'3' %}
|
|
<li class="page-item {% if logs.number == i %}active{% endif %}">
|
|
<a class="page-link"
|
|
href="?tab={{ active_tab }}&page={{ i }}">
|
|
{{ i }}
|
|
</a>
|
|
</li>
|
|
{% elif i == logs.number|add:'-3' or i == logs.number|add:'3' %}
|
|
<li class="page-item disabled"><span class="page-link">...</span></li>
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
<li class="page-item {% if not logs.has_next %}disabled{% endif %}">
|
|
<a class="page-link"
|
|
href="?tab={{ active_tab }}{% if logs.has_next %}&page={{ logs.next_page_number }}{% endif %}"
|
|
aria-label="Next">
|
|
<span aria-hidden="true">»</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</nav>
|
|
{% endif %}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %} |