194 lines
12 KiB
HTML
194 lines
12 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
{% load i18n %}
|
|
|
|
{% block title %}{% trans "Audit Dashboard" %}{% endblock %}
|
|
|
|
{% block content %}
|
|
<!-- Breadcrumb -->
|
|
<nav aria-label="breadcrumb" class="mb-4">
|
|
<ol class="flex items-center gap-2 text-sm">
|
|
<li>
|
|
<a href="{% url 'settings' %}" class="text-gray-600 hover:text-temple-red transition">{% trans "Settings" %}</a>
|
|
</li>
|
|
<li class="text-gray-400">/</li>
|
|
<li class="font-semibold text-temple-red">{% trans "System Activity" %}</li>
|
|
</ol>
|
|
</nav>
|
|
|
|
<!-- Header -->
|
|
<div class="mb-4">
|
|
<h1 class="text-2xl font-bold text-gray-900 flex items-center gap-2">
|
|
<i data-lucide="shield" class="w-6 h-6 text-temple-red"></i>
|
|
{% trans "System Activity Logs" %}
|
|
</h1>
|
|
</div>
|
|
|
|
<!-- Summary Alert -->
|
|
<div class="bg-temple-red/10 border-l-4 border-temple-red p-4 mb-6 rounded-r-lg">
|
|
<div class="text-sm font-semibold text-gray-800 mb-1">
|
|
{% trans "Viewing Logs" %}: <strong>{{ tab_title }}</strong>
|
|
</div>
|
|
<p class="text-sm text-gray-700">
|
|
{% trans "Displaying" %}: <strong>{{ logs.start_index }}-{{ logs.end_index }}</strong> {% trans "of" %}
|
|
<strong>{{ total_count }}</strong> {% trans "total records." %}
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Audit Card -->
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-200 min-h-[60vh]">
|
|
|
|
<!-- Tabs -->
|
|
<div class="flex border-b border-gray-200 px-3 pt-4 rounded-t-xl">
|
|
<a class="px-4 py-2 text-sm font-medium transition border-b-3 {% if active_tab == 'crud' %}text-temple-red border-temple-red{% else %}text-gray-600 border-transparent hover:text-temple-red hover:border-gray-300{% endif %}"
|
|
href="?tab=crud">
|
|
<i data-lucide="database" class="w-4 h-4 inline mr-2"></i>{% trans "Model Changes (CRUD)" %}
|
|
</a>
|
|
<a class="px-4 py-2 text-sm font-medium transition border-b-3 {% if active_tab == 'login' %}text-temple-red border-temple-red{% else %}text-gray-600 border-transparent hover:text-temple-red hover:border-gray-300{% endif %}"
|
|
href="?tab=login">
|
|
<i data-lucide="user-lock" class="w-4 h-4 inline mr-2"></i>{% trans "User Authentication" %}
|
|
</a>
|
|
<a class="px-4 py-2 text-sm font-medium transition border-b-3 {% if active_tab == 'request' %}text-temple-red border-temple-red{% else %}text-gray-600 border-transparent hover:text-temple-red hover:border-gray-300{% endif %}"
|
|
href="?tab=request">
|
|
<i data-lucide="globe" class="w-4 h-4 inline mr-2"></i>{% trans "HTTP Requests" %}
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Tab Content -->
|
|
<div class="p-4">
|
|
<div class="overflow-x-auto">
|
|
<table class="w-full text-sm">
|
|
|
|
<thead>
|
|
{% if active_tab == 'crud' %}
|
|
<tr class="bg-gray-50">
|
|
<th class="px-4 py-3 text-left font-semibold text-gray-700">{% trans "Date/Time" %}</th>
|
|
<th class="px-4 py-3 text-left font-semibold text-gray-700">{% trans "User" %}</th>
|
|
<th class="px-4 py-3 text-left font-semibold text-gray-700">{% trans "Action" %}</th>
|
|
<th class="px-4 py-3 text-left font-semibold text-gray-700">{% trans "Model" %}</th>
|
|
<th class="px-4 py-3 text-left font-semibold text-gray-700">{% trans "Object PK" %}</th>
|
|
<th class="px-4 py-3 text-left font-semibold text-gray-700">{% trans "Changes" %}</th>
|
|
</tr>
|
|
{% elif active_tab == 'login' %}
|
|
<tr class="bg-gray-50">
|
|
<th class="px-4 py-3 text-left font-semibold text-gray-700">{% trans "Date/Time" %}</th>
|
|
<th class="px-4 py-3 text-left font-semibold text-gray-700">{% trans "User" %}</th>
|
|
<th class="px-4 py-3 text-left font-semibold text-gray-700">{% trans "Type" %}</th>
|
|
<th class="px-4 py-3 text-left font-semibold text-gray-700">{% trans "Status" %}</th>
|
|
<th class="px-4 py-3 text-left font-semibold text-gray-700">{% trans "IP Address" %}</th>
|
|
</tr>
|
|
{% elif active_tab == 'request' %}
|
|
<tr class="bg-gray-50">
|
|
<th class="px-4 py-3 text-left font-semibold text-gray-700">{% trans "Date/Time" %}</th>
|
|
<th class="px-4 py-3 text-left font-semibold text-gray-700">{% trans "User" %}</th>
|
|
<th class="px-4 py-3 text-left font-semibold text-gray-700">{% trans "Method" %}</th>
|
|
<th class="px-4 py-3 text-left font-semibold text-gray-700">{% trans "Path" %}</th>
|
|
</tr>
|
|
{% endif %}
|
|
</thead>
|
|
|
|
<tbody class="divide-y divide-gray-100">
|
|
{% for log in logs.object_list %}
|
|
{% if active_tab == 'crud' %}
|
|
<tr class="hover:bg-gray-50">
|
|
<td class="px-4 py-3 text-gray-900">{{ log.datetime|date:"Y-m-d H:i:s" }}</td>
|
|
<td class="px-4 py-3 text-gray-900">{{ log.user.email|default:"N/A" }}</td>
|
|
<td class="px-4 py-3">
|
|
{% if log.event_type == 1 %}
|
|
<span class="inline-flex items-center gap-1 px-2.5 py-1 bg-green-100 text-green-800 rounded-full text-xs font-medium"><i data-lucide="plus" class="w-3 h-3"></i>{% trans "CREATE" %}</span>
|
|
{% elif log.event_type == 2 %}
|
|
<span class="inline-flex items-center gap-1 px-2.5 py-1 bg-yellow-100 text-yellow-800 rounded-full text-xs font-medium"><i data-lucide="edit" class="w-3 h-3"></i>{% trans "UPDATE" %}</span>
|
|
{% else %}
|
|
<span class="inline-flex items-center gap-1 px-2.5 py-1 bg-red-100 text-red-800 rounded-full text-xs font-medium"><i data-lucide="trash-2" class="w-3 h-3"></i>{% trans "DELETE" %}</span>
|
|
{% endif %}
|
|
</td>
|
|
<td class="px-4 py-3"><code class="bg-gray-100 px-2 py-1 rounded text-xs">{{ log.content_type.app_label }}.{{ log.content_type.model }}</code></td>
|
|
<td class="px-4 py-3 text-gray-900">{{ log.object_id }}</td>
|
|
<td class="px-4 py-3">
|
|
<pre class="bg-gray-100 p-2 rounded text-xs overflow-x-auto max-h-20 overflow-y-auto">{{ log.changed_fields }}</pre>
|
|
</td>
|
|
</tr>
|
|
|
|
{% elif active_tab == 'login' %}
|
|
<tr class="hover:bg-gray-50">
|
|
<td class="px-4 py-3 text-gray-900">{{ log.datetime|date:"Y-m-d H:i:s" }}</td>
|
|
<td class="px-4 py-3 text-gray-900">
|
|
{% with user_obj=log.user %}
|
|
{% if user_obj %}
|
|
{{ user_obj.get_full_name|default:user_obj.username }}
|
|
{% else %}
|
|
<span class="text-red-600 font-semibold">{{ log.username|default:"N/A" }}</span>
|
|
{% endif %}
|
|
{% endwith %}
|
|
</td>
|
|
<td class="px-4 py-3">
|
|
<span class="inline-block px-2.5 py-1 bg-gray-700 text-white rounded-full text-xs font-medium">
|
|
{% if log.login_type == 0 %}{% trans "Login" %}
|
|
{% elif log.login_type == 1 %}{% trans "Logout" %}
|
|
{% else %}{% trans "Failed Login" %}{% endif %}
|
|
</span>
|
|
</td>
|
|
<td class="px-4 py-3">
|
|
{% if log.login_type == 2 %}
|
|
<span class="inline-flex items-center gap-1 text-red-600"><i data-lucide="x-circle" class="w-4 h-4"></i>{% trans "Failed" %}</span>
|
|
{% else %}
|
|
<span class="inline-flex items-center gap-1 text-green-600"><i data-lucide="check-circle" class="w-4 h-4"></i>{% trans "Success" %}</span>
|
|
{% endif %}
|
|
</td>
|
|
<td class="px-4 py-3 text-gray-900">{{ log.remote_ip|default:"Unknown" }}</td>
|
|
</tr>
|
|
|
|
{% elif active_tab == 'request' %}
|
|
<tr class="hover:bg-gray-50">
|
|
<td class="px-4 py-3 text-gray-900">{{ log.datetime|date:"Y-m-d H:i:s" }}</td>
|
|
<td class="px-4 py-3 text-gray-900">{{ log.user.get_full_name|default:log.user.email|default:"Anonymous" }}</td>
|
|
<td class="px-4 py-3">
|
|
<span class="inline-block px-2.5 py-1 bg-gray-700 text-white rounded-full text-xs font-medium">{{ log.method }}</span>
|
|
</td>
|
|
<td class="px-4 py-3"><code class="bg-gray-100 px-2 py-1 rounded text-xs break-all">{{ log.url}}</code></td>
|
|
</tr>
|
|
{% endif %}
|
|
{% empty %}
|
|
<tr>
|
|
<td colspan="6" class="px-4 py-12 text-center text-gray-500">
|
|
<i data-lucide="info" class="w-5 h-5 inline mr-2"></i>{% trans "No logs found for this section or database is empty." %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
{% if logs.has_other_pages %}
|
|
<div class="flex justify-end items-center gap-2 pt-4">
|
|
<a href="?tab={{ active_tab }}{% if logs.has_previous %}&page={{ logs.previous_page_number }}{% endif %}"
|
|
class="px-3 py-2 text-sm font-medium rounded-lg {% if logs.has_previous %}bg-white border border-gray-300 text-temple-red hover:bg-gray-50{% else %}bg-gray-100 text-gray-400 cursor-not-allowed{% endif %} transition">
|
|
<i data-lucide="chevron-left" class="w-4 h-4 inline"></i>
|
|
</a>
|
|
|
|
{% for i in logs.paginator.page_range %}
|
|
{% if i > logs.number|add:'-3' and i < logs.number|add:'3' %}
|
|
<a href="?tab={{ active_tab }}&page={{ i }}"
|
|
class="px-3 py-2 text-sm font-medium rounded-lg {% if logs.number == i %}bg-temple-red text-white{% else %}bg-white border border-gray-300 text-temple-red hover:bg-gray-50{% endif %} transition">
|
|
{{ i }}
|
|
</a>
|
|
{% elif i == logs.number|add:'-3' or i == logs.number|add:'3' %}
|
|
<span class="px-3 py-2 text-sm text-gray-400">...</span>
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
<a href="?tab={{ active_tab }}{% if logs.has_next %}&page={{ logs.next_page_number }}{% endif %}"
|
|
class="px-3 py-2 text-sm font-medium rounded-lg {% if logs.has_next %}bg-white border border-gray-300 text-temple-red hover:bg-gray-50{% else %}bg-gray-100 text-gray-400 cursor-not-allowed{% endif %} transition">
|
|
<i data-lucide="chevron-right" class="w-4 h-4 inline"></i>
|
|
</a>
|
|
</div>
|
|
{% endif %}
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
lucide.createIcons();
|
|
</script>
|
|
{% endblock %} |