681 lines
29 KiB
HTML
681 lines
29 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}API Logs - {{ block.super }}{% endblock %}
|
|
|
|
{% block css %}
|
|
<style>
|
|
.logs-header {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
color: white;
|
|
border-radius: 10px;
|
|
padding: 30px;
|
|
margin-bottom: 30px;
|
|
}
|
|
.log-entry {
|
|
background: white;
|
|
border-radius: 8px;
|
|
padding: 15px;
|
|
margin: 10px 0;
|
|
border-left: 4px solid #007bff;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
transition: all 0.3s ease;
|
|
}
|
|
.log-entry:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
|
|
}
|
|
.log-success { border-left-color: #28a745; }
|
|
.log-error { border-left-color: #dc3545; }
|
|
.log-warning { border-left-color: #ffc107; }
|
|
.log-info { border-left-color: #17a2b8; }
|
|
.log-debug { border-left-color: #6c757d; }
|
|
.log-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 10px;
|
|
}
|
|
.log-timestamp {
|
|
color: #6c757d;
|
|
font-size: 0.9rem;
|
|
}
|
|
.log-level {
|
|
padding: 2px 8px;
|
|
border-radius: 12px;
|
|
font-size: 0.8rem;
|
|
font-weight: bold;
|
|
}
|
|
.level-success { background: #d4edda; color: #155724; }
|
|
.level-error { background: #f8d7da; color: #721c24; }
|
|
.level-warning { background: #fff3cd; color: #856404; }
|
|
.level-info { background: #d1ecf1; color: #0c5460; }
|
|
.level-debug { background: #e2e3e5; color: #383d41; }
|
|
.log-content {
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 0.9rem;
|
|
background: #f8f9fa;
|
|
padding: 10px;
|
|
border-radius: 4px;
|
|
margin: 10px 0;
|
|
white-space: pre-wrap;
|
|
word-break: break-all;
|
|
}
|
|
.log-details {
|
|
display: none;
|
|
margin-top: 15px;
|
|
padding-top: 15px;
|
|
border-top: 1px solid #dee2e6;
|
|
}
|
|
.detail-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 5px 0;
|
|
border-bottom: 1px solid #f1f1f1;
|
|
}
|
|
.detail-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
.filter-panel {
|
|
background: white;
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
margin-bottom: 20px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
.stats-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 15px;
|
|
margin: 20px 0;
|
|
}
|
|
.stat-card {
|
|
background: white;
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
text-align: center;
|
|
border: 1px solid #dee2e6;
|
|
}
|
|
.stat-value {
|
|
font-size: 2rem;
|
|
font-weight: bold;
|
|
color: #007bff;
|
|
}
|
|
.stat-label {
|
|
color: #6c757d;
|
|
font-size: 0.9rem;
|
|
margin-top: 5px;
|
|
}
|
|
.response-time {
|
|
font-weight: bold;
|
|
}
|
|
.response-fast { color: #28a745; }
|
|
.response-medium { color: #ffc107; }
|
|
.response-slow { color: #dc3545; }
|
|
.status-code {
|
|
padding: 2px 6px;
|
|
border-radius: 4px;
|
|
font-size: 0.8rem;
|
|
font-weight: bold;
|
|
}
|
|
.status-2xx { background: #d4edda; color: #155724; }
|
|
.status-3xx { background: #d1ecf1; color: #0c5460; }
|
|
.status-4xx { background: #fff3cd; color: #856404; }
|
|
.status-5xx { background: #f8d7da; color: #721c24; }
|
|
.search-highlight {
|
|
background: #fff3cd;
|
|
padding: 1px 3px;
|
|
border-radius: 2px;
|
|
}
|
|
.log-actions {
|
|
display: flex;
|
|
gap: 10px;
|
|
margin-top: 10px;
|
|
}
|
|
.btn-expand {
|
|
font-size: 0.8rem;
|
|
padding: 2px 8px;
|
|
}
|
|
.pagination-wrapper {
|
|
display: flex;
|
|
justify-content: between;
|
|
align-items: center;
|
|
margin: 20px 0;
|
|
}
|
|
.real-time-indicator {
|
|
display: flex;
|
|
align-items: center;
|
|
color: #28a745;
|
|
font-size: 0.9rem;
|
|
}
|
|
.pulse-dot {
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 50%;
|
|
background: #28a745;
|
|
margin-right: 8px;
|
|
animation: pulse 1.5s infinite;
|
|
}
|
|
@keyframes pulse {
|
|
0% { opacity: 1; }
|
|
50% { opacity: 0.5; }
|
|
100% { opacity: 1; }
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="content">
|
|
<div class="container-fluid">
|
|
<!-- Page Header -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="page-header">
|
|
<div class="page-title">
|
|
<h4><i class="fas fa-list-alt"></i> API Logs</h4>
|
|
<h6>Monitor and analyze API request logs</h6>
|
|
</div>
|
|
<div class="page-btn">
|
|
<button class="btn btn-primary me-2" id="refreshLogs">
|
|
<i class="fas fa-sync-alt"></i> Refresh
|
|
</button>
|
|
<button class="btn btn-success me-2" id="exportLogs">
|
|
<i class="fas fa-download"></i> Export
|
|
</button>
|
|
<button class="btn btn-info" id="realTimeToggle">
|
|
<i class="fas fa-play"></i> Real-time
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Logs Header -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="logs-header">
|
|
<div class="row">
|
|
<div class="col-md-8">
|
|
<h5><i class="fas fa-server"></i> API Request Monitoring</h5>
|
|
<p>Real-time monitoring of API requests, responses, and system integration logs</p>
|
|
<div class="real-time-indicator" id="realTimeStatus" style="display: none;">
|
|
<div class="pulse-dot"></div>
|
|
<span>Real-time monitoring active</span>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="stats-grid">
|
|
<div class="stat-card">
|
|
<div class="stat-value">1,247</div>
|
|
<div class="stat-label">Total Requests</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-value">23</div>
|
|
<div class="stat-label">Errors</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Filter Panel -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="filter-panel">
|
|
<div class="row">
|
|
<div class="col-md-2">
|
|
<label>Log Level</label>
|
|
<select class="form-control" id="levelFilter">
|
|
<option value="">All Levels</option>
|
|
<option value="success">Success</option>
|
|
<option value="error">Error</option>
|
|
<option value="warning">Warning</option>
|
|
<option value="info">Info</option>
|
|
<option value="debug">Debug</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label>API Endpoint</label>
|
|
<select class="form-control" id="endpointFilter">
|
|
<option value="">All Endpoints</option>
|
|
<option value="/api/patients">/api/patients</option>
|
|
<option value="/api/appointments">/api/appointments</option>
|
|
<option value="/api/lab-results">/api/lab-results</option>
|
|
<option value="/api/medications">/api/medications</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label>Status Code</label>
|
|
<select class="form-control" id="statusFilter">
|
|
<option value="">All Status</option>
|
|
<option value="2xx">2xx Success</option>
|
|
<option value="3xx">3xx Redirect</option>
|
|
<option value="4xx">4xx Client Error</option>
|
|
<option value="5xx">5xx Server Error</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label>Time Range</label>
|
|
<select class="form-control" id="timeFilter">
|
|
<option value="1h">Last Hour</option>
|
|
<option value="6h">Last 6 Hours</option>
|
|
<option value="24h">Last 24 Hours</option>
|
|
<option value="7d">Last 7 Days</option>
|
|
<option value="30d">Last 30 Days</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label>Search</label>
|
|
<input type="text" class="form-control" id="searchFilter" placeholder="Search logs...">
|
|
</div>
|
|
<div class="col-md-1">
|
|
<label> </label>
|
|
<button class="btn btn-primary form-control" id="applyFilters">
|
|
<i class="fas fa-filter"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- API Logs -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5><i class="fas fa-terminal"></i> Request Logs</h5>
|
|
<div class="card-tools">
|
|
<span class="badge bg-primary">Live</span>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="logsContainer">
|
|
<!-- Success Log Entry -->
|
|
<div class="log-entry log-success">
|
|
<div class="log-header">
|
|
<div>
|
|
<span class="log-level level-success">SUCCESS</span>
|
|
<strong>GET /api/patients/12345</strong>
|
|
<span class="status-code status-2xx">200</span>
|
|
<span class="response-time response-fast">145ms</span>
|
|
</div>
|
|
<div class="log-timestamp">2024-01-20 14:32:15</div>
|
|
</div>
|
|
<div class="log-content">Patient data retrieved successfully for ID: 12345</div>
|
|
<div class="log-actions">
|
|
<button class="btn btn-sm btn-outline-primary btn-expand" onclick="toggleDetails(this)">
|
|
<i class="fas fa-chevron-down"></i> Details
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-secondary">
|
|
<i class="fas fa-copy"></i> Copy
|
|
</button>
|
|
</div>
|
|
<div class="log-details">
|
|
<div class="detail-item">
|
|
<span><strong>Request ID:</strong></span>
|
|
<span>req_1642684335_abc123</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span><strong>User Agent:</strong></span>
|
|
<span>Hospital-Management-System/1.0</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span><strong>IP Address:</strong></span>
|
|
<span>192.168.1.100</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span><strong>Response Size:</strong></span>
|
|
<span>2.3 KB</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span><strong>Headers:</strong></span>
|
|
<span>Content-Type: application/json, Authorization: Bearer ***</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Error Log Entry -->
|
|
<div class="log-entry log-error">
|
|
<div class="log-header">
|
|
<div>
|
|
<span class="log-level level-error">ERROR</span>
|
|
<strong>POST /api/appointments</strong>
|
|
<span class="status-code status-4xx">400</span>
|
|
<span class="response-time response-medium">892ms</span>
|
|
</div>
|
|
<div class="log-timestamp">2024-01-20 14:31:42</div>
|
|
</div>
|
|
<div class="log-content">Validation error: Missing required field 'patient_id'</div>
|
|
<div class="log-actions">
|
|
<button class="btn btn-sm btn-outline-primary btn-expand" onclick="toggleDetails(this)">
|
|
<i class="fas fa-chevron-down"></i> Details
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-danger">
|
|
<i class="fas fa-bug"></i> Debug
|
|
</button>
|
|
</div>
|
|
<div class="log-details">
|
|
<div class="detail-item">
|
|
<span><strong>Error Code:</strong></span>
|
|
<span>VALIDATION_ERROR</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span><strong>Request Body:</strong></span>
|
|
<span>{"appointment_date": "2024-01-25", "doctor_id": 123}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span><strong>Stack Trace:</strong></span>
|
|
<span>ValidationError at line 45 in appointments/views.py</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Warning Log Entry -->
|
|
<div class="log-entry log-warning">
|
|
<div class="log-header">
|
|
<div>
|
|
<span class="log-level level-warning">WARNING</span>
|
|
<strong>GET /api/lab-results</strong>
|
|
<span class="status-code status-2xx">200</span>
|
|
<span class="response-time response-slow">3.2s</span>
|
|
</div>
|
|
<div class="log-timestamp">2024-01-20 14:30:18</div>
|
|
</div>
|
|
<div class="log-content">Slow query detected: Lab results query took 3.2 seconds</div>
|
|
<div class="log-actions">
|
|
<button class="btn btn-sm btn-outline-primary btn-expand" onclick="toggleDetails(this)">
|
|
<i class="fas fa-chevron-down"></i> Details
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-warning">
|
|
<i class="fas fa-tachometer-alt"></i> Optimize
|
|
</button>
|
|
</div>
|
|
<div class="log-details">
|
|
<div class="detail-item">
|
|
<span><strong>Query:</strong></span>
|
|
<span>SELECT * FROM lab_results WHERE patient_id = 12345</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span><strong>Records Returned:</strong></span>
|
|
<span>1,247</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span><strong>Suggestion:</strong></span>
|
|
<span>Add pagination or date range filter</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Info Log Entry -->
|
|
<div class="log-entry log-info">
|
|
<div class="log-header">
|
|
<div>
|
|
<span class="log-level level-info">INFO</span>
|
|
<strong>PUT /api/medications/789</strong>
|
|
<span class="status-code status-2xx">200</span>
|
|
<span class="response-time response-fast">234ms</span>
|
|
</div>
|
|
<div class="log-timestamp">2024-01-20 14:29:55</div>
|
|
</div>
|
|
<div class="log-content">Medication record updated successfully</div>
|
|
<div class="log-actions">
|
|
<button class="btn btn-sm btn-outline-primary btn-expand" onclick="toggleDetails(this)">
|
|
<i class="fas fa-chevron-down"></i> Details
|
|
</button>
|
|
</div>
|
|
<div class="log-details">
|
|
<div class="detail-item">
|
|
<span><strong>Updated Fields:</strong></span>
|
|
<span>dosage, frequency, end_date</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span><strong>Updated By:</strong></span>
|
|
<span>Dr. Smith (user_id: 456)</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Debug Log Entry -->
|
|
<div class="log-entry log-debug">
|
|
<div class="log-header">
|
|
<div>
|
|
<span class="log-level level-debug">DEBUG</span>
|
|
<strong>GET /api/system/health</strong>
|
|
<span class="status-code status-2xx">200</span>
|
|
<span class="response-time response-fast">12ms</span>
|
|
</div>
|
|
<div class="log-timestamp">2024-01-20 14:29:30</div>
|
|
</div>
|
|
<div class="log-content">System health check completed - all services operational</div>
|
|
<div class="log-actions">
|
|
<button class="btn btn-sm btn-outline-primary btn-expand" onclick="toggleDetails(this)">
|
|
<i class="fas fa-chevron-down"></i> Details
|
|
</button>
|
|
</div>
|
|
<div class="log-details">
|
|
<div class="detail-item">
|
|
<span><strong>Database:</strong></span>
|
|
<span>Connected (5ms)</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span><strong>Cache:</strong></span>
|
|
<span>Connected (2ms)</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span><strong>External APIs:</strong></span>
|
|
<span>All responding</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
<div class="pagination-wrapper">
|
|
<div>
|
|
<span class="text-muted">Showing 1-10 of 1,247 entries</span>
|
|
</div>
|
|
<nav>
|
|
<ul class="pagination pagination-sm">
|
|
<li class="page-item disabled">
|
|
<a class="page-link" href="#" tabindex="-1">Previous</a>
|
|
</li>
|
|
<li class="page-item active">
|
|
<a class="page-link" href="#">1</a>
|
|
</li>
|
|
<li class="page-item">
|
|
<a class="page-link" href="#">2</a>
|
|
</li>
|
|
<li class="page-item">
|
|
<a class="page-link" href="#">3</a>
|
|
</li>
|
|
<li class="page-item">
|
|
<a class="page-link" href="#">Next</a>
|
|
</li>
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<script>
|
|
$(document).ready(function() {
|
|
var realTimeActive = false;
|
|
var realTimeInterval;
|
|
|
|
// Toggle log details
|
|
window.toggleDetails = function(button) {
|
|
var details = $(button).closest('.log-entry').find('.log-details');
|
|
var icon = $(button).find('i');
|
|
|
|
if (details.is(':visible')) {
|
|
details.slideUp();
|
|
icon.removeClass('fa-chevron-up').addClass('fa-chevron-down');
|
|
} else {
|
|
details.slideDown();
|
|
icon.removeClass('fa-chevron-down').addClass('fa-chevron-up');
|
|
}
|
|
};
|
|
|
|
// Real-time toggle
|
|
$('#realTimeToggle').click(function() {
|
|
if (!realTimeActive) {
|
|
startRealTime();
|
|
} else {
|
|
stopRealTime();
|
|
}
|
|
});
|
|
|
|
function startRealTime() {
|
|
realTimeActive = true;
|
|
$('#realTimeToggle').html('<i class="fas fa-pause"></i> Stop Real-time');
|
|
$('#realTimeStatus').show();
|
|
|
|
// Simulate real-time log updates
|
|
realTimeInterval = setInterval(function() {
|
|
addNewLogEntry();
|
|
}, 5000);
|
|
}
|
|
|
|
function stopRealTime() {
|
|
realTimeActive = false;
|
|
clearInterval(realTimeInterval);
|
|
$('#realTimeToggle').html('<i class="fas fa-play"></i> Real-time');
|
|
$('#realTimeStatus').hide();
|
|
}
|
|
|
|
function addNewLogEntry() {
|
|
var logTypes = ['success', 'error', 'warning', 'info', 'debug'];
|
|
var endpoints = ['/api/patients', '/api/appointments', '/api/lab-results', '/api/medications'];
|
|
var methods = ['GET', 'POST', 'PUT', 'DELETE'];
|
|
|
|
var type = logTypes[Math.floor(Math.random() * logTypes.length)];
|
|
var endpoint = endpoints[Math.floor(Math.random() * endpoints.length)];
|
|
var method = methods[Math.floor(Math.random() * methods.length)];
|
|
var timestamp = new Date().toLocaleString();
|
|
|
|
var statusCode = type === 'error' ? '500' : '200';
|
|
var statusClass = type === 'error' ? 'status-5xx' : 'status-2xx';
|
|
var responseTime = Math.floor(Math.random() * 1000) + 50;
|
|
var responseClass = responseTime < 200 ? 'response-fast' : responseTime < 1000 ? 'response-medium' : 'response-slow';
|
|
|
|
var newEntry = `
|
|
<div class="log-entry log-${type}" style="display: none;">
|
|
<div class="log-header">
|
|
<div>
|
|
<span class="log-level level-${type}">${type.toUpperCase()}</span>
|
|
<strong>${method} ${endpoint}</strong>
|
|
<span class="status-code ${statusClass}">${statusCode}</span>
|
|
<span class="response-time ${responseClass}">${responseTime}ms</span>
|
|
</div>
|
|
<div class="log-timestamp">${timestamp}</div>
|
|
</div>
|
|
<div class="log-content">New ${type} log entry generated</div>
|
|
<div class="log-actions">
|
|
<button class="btn btn-sm btn-outline-primary btn-expand" onclick="toggleDetails(this)">
|
|
<i class="fas fa-chevron-down"></i> Details
|
|
</button>
|
|
</div>
|
|
<div class="log-details">
|
|
<div class="detail-item">
|
|
<span><strong>Request ID:</strong></span>
|
|
<span>req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
$('#logsContainer').prepend(newEntry);
|
|
$('#logsContainer .log-entry:first').slideDown();
|
|
|
|
// Remove old entries to prevent memory issues
|
|
if ($('#logsContainer .log-entry').length > 20) {
|
|
$('#logsContainer .log-entry:last').remove();
|
|
}
|
|
}
|
|
|
|
// Filter functionality
|
|
$('#applyFilters').click(function() {
|
|
var level = $('#levelFilter').val();
|
|
var endpoint = $('#endpointFilter').val();
|
|
var status = $('#statusFilter').val();
|
|
var search = $('#searchFilter').val().toLowerCase();
|
|
|
|
$('.log-entry').each(function() {
|
|
var show = true;
|
|
|
|
if (level && !$(this).hasClass('log-' + level)) {
|
|
show = false;
|
|
}
|
|
|
|
if (endpoint && !$(this).text().includes(endpoint)) {
|
|
show = false;
|
|
}
|
|
|
|
if (status && !$(this).find('.status-code').hasClass('status-' + status)) {
|
|
show = false;
|
|
}
|
|
|
|
if (search && !$(this).text().toLowerCase().includes(search)) {
|
|
show = false;
|
|
}
|
|
|
|
$(this).toggle(show);
|
|
});
|
|
});
|
|
|
|
// Search highlighting
|
|
$('#searchFilter').on('input', function() {
|
|
var searchTerm = $(this).val();
|
|
if (searchTerm.length > 2) {
|
|
highlightSearchTerm(searchTerm);
|
|
} else {
|
|
removeHighlights();
|
|
}
|
|
});
|
|
|
|
function highlightSearchTerm(term) {
|
|
$('.log-content').each(function() {
|
|
var content = $(this).html();
|
|
var regex = new RegExp('(' + term + ')', 'gi');
|
|
var highlighted = content.replace(regex, '<span class="search-highlight">$1</span>');
|
|
$(this).html(highlighted);
|
|
});
|
|
}
|
|
|
|
function removeHighlights() {
|
|
$('.search-highlight').each(function() {
|
|
$(this).replaceWith($(this).text());
|
|
});
|
|
}
|
|
|
|
// Refresh logs
|
|
$('#refreshLogs').click(function() {
|
|
location.reload();
|
|
});
|
|
|
|
// Export logs
|
|
$('#exportLogs').click(function() {
|
|
alert('Exporting API logs...');
|
|
});
|
|
|
|
// Copy log content
|
|
$(document).on('click', '.btn-outline-secondary', function() {
|
|
var logContent = $(this).closest('.log-entry').find('.log-content').text();
|
|
navigator.clipboard.writeText(logContent).then(function() {
|
|
alert('Log content copied to clipboard');
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %}
|
|
|