2025-08-12 13:33:25 +03:00

485 lines
20 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}API Endpoints - Integration{% endblock %}
{% block css %}
<link href="{% static 'assets/plugins/datatables.net-bs5/css/dataTables.bootstrap5.min.css' %}" rel="stylesheet" />
<link href="{% static 'assets/plugins/datatables.net-responsive-bs5/css/responsive.bootstrap5.min.css' %}" rel="stylesheet" />
{% endblock %}
{% block content %}
<!-- BEGIN breadcrumb -->
<ol class="breadcrumb float-xl-end">
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
<li class="breadcrumb-item"><a href="{% url 'integration:dashboard' %}">Integration</a></li>
<li class="breadcrumb-item active">API Endpoints</li>
</ol>
<!-- END breadcrumb -->
<!-- BEGIN page-header -->
<h1 class="page-header">
API Endpoints
<small>External System Integration Points</small>
</h1>
<!-- END page-header -->
<!-- BEGIN panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">API Endpoint Management</h4>
<div class="panel-heading-btn">
<a href="{% url 'integration:api_endpoint_create' %}" class="btn btn-xs btn-success me-2">
<i class="fa fa-plus"></i> Add Endpoint
</a>
<button class="btn btn-xs btn-primary me-2" onclick="testAllEndpoints()">
<i class="fa fa-play"></i> Test All
</button>
<button class="btn btn-xs btn-info me-2" data-bs-toggle="modal" data-bs-target="#bulk-actions-modal">
<i class="fa fa-cogs"></i> Bulk Actions
</button>
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>
</div>
</div>
<div class="panel-body">
<!-- Statistics Cards -->
<div class="row mb-4">
<div class="col-md-3">
<div class="card border-primary">
<div class="card-body text-center">
<div class="fs-24px fw-bold text-primary">{{ total_endpoints }}</div>
<div class="small text-muted">Total Endpoints</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-success">
<div class="card-body text-center">
<div class="fs-24px fw-bold text-success">{{ active_endpoints }}</div>
<div class="small text-muted">Active</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-warning">
<div class="card-body text-center">
<div class="fs-24px fw-bold text-warning">{{ warning_endpoints }}</div>
<div class="small text-muted">Warnings</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-danger">
<div class="card-body text-center">
<div class="fs-24px fw-bold text-danger">{{ failed_endpoints }}</div>
<div class="small text-muted">Failed</div>
</div>
</div>
</div>
</div>
<!-- Quick Filters -->
<div class="row mb-3">
<div class="col-md-12">
<div class="btn-group me-2" role="group">
<button type="button" class="btn btn-outline-primary active" onclick="filterByStatus('all')">All Status</button>
<button type="button" class="btn btn-outline-primary" onclick="filterByStatus('ACTIVE')">Active</button>
<button type="button" class="btn btn-outline-primary" onclick="filterByStatus('INACTIVE')">Inactive</button>
<button type="button" class="btn btn-outline-primary" onclick="filterByStatus('TESTING')">Testing</button>
</div>
<div class="btn-group me-2" role="group">
<button type="button" class="btn btn-outline-secondary active" onclick="filterByMethod('all')">All Methods</button>
<button type="button" class="btn btn-outline-secondary" onclick="filterByMethod('GET')">GET</button>
<button type="button" class="btn btn-outline-secondary" onclick="filterByMethod('POST')">POST</button>
<button type="button" class="btn btn-outline-secondary" onclick="filterByMethod('PUT')">PUT</button>
<button type="button" class="btn btn-outline-secondary" onclick="filterByMethod('DELETE')">DELETE</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-outline-info active" onclick="filterBySystem('all')">All Systems</button>
{% for system in external_systems %}
<button type="button" class="btn btn-outline-info" onclick="filterBySystem('{{ system.id }}')">{{ system.name }}</button>
{% endfor %}
</div>
</div>
</div>
<!-- API Endpoints Table -->
<div class="table-responsive">
<table id="endpoints-table" class="table table-striped table-bordered align-middle">
<thead>
<tr>
<th width="5%">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="select-all">
</div>
</th>
<th width="20%">Name</th>
<th width="15%">System</th>
<th width="25%">URL</th>
<th width="8%">Method</th>
<th width="10%">Status</th>
<th width="10%">Last Test</th>
<th width="7%">Actions</th>
</tr>
</thead>
<tbody>
{% for endpoint in object_list %}
<tr data-status="{{ endpoint.status }}" data-method="{{ endpoint.method }}" data-system="{{ endpoint.external_system.id }}">
<td>
<div class="form-check">
<input class="form-check-input row-checkbox" type="checkbox" value="{{ endpoint.id }}">
</div>
</td>
<td>
<div class="fw-bold">{{ endpoint.name }}</div>
<div class="small text-muted">{{ endpoint.description|truncatechars:50 }}</div>
</td>
<td>
<a href="{% url 'integration:external_system_detail' endpoint.external_system.pk %}" class="text-decoration-none">
{{ endpoint.external_system.name }}
</a>
</td>
<td>
<code class="small">{{ endpoint.url|truncatechars:40 }}</code>
</td>
<td>
<span class="badge bg-{% if endpoint.method == 'GET' %}primary{% elif endpoint.method == 'POST' %}success{% elif endpoint.method == 'PUT' %}warning{% elif endpoint.method == 'DELETE' %}danger{% else %}secondary{% endif %}">
{{ endpoint.method }}
</span>
</td>
<td>
<span class="badge bg-{% if endpoint.status == 'ACTIVE' %}success{% elif endpoint.status == 'INACTIVE' %}secondary{% elif endpoint.status == 'TESTING' %}warning{% else %}danger{% endif %}">
{{ endpoint.get_status_display }}
</span>
{% if endpoint.health_status %}
<div class="small mt-1">
<span class="badge bg-{% if endpoint.health_status == 'HEALTHY' %}success{% elif endpoint.health_status == 'WARNING' %}warning{% else %}danger{% endif %} badge-sm">
{{ endpoint.health_status }}
</span>
</div>
{% endif %}
</td>
<td>
{% if endpoint.last_tested %}
<div class="small">{{ endpoint.last_tested|date:"M d, Y" }}</div>
<div class="small text-muted">{{ endpoint.last_tested|date:"H:i" }}</div>
{% else %}
<span class="text-muted">Never</span>
{% endif %}
</td>
<td>
<div class="btn-group btn-group-sm">
<a href="{% url 'integration:api_endpoint_detail' endpoint.pk %}" class="btn btn-outline-primary btn-sm" title="View Details">
<i class="fa fa-eye"></i>
</a>
<button class="btn btn-outline-success btn-sm" onclick="testEndpoint({{ endpoint.id }})" title="Test Endpoint">
<i class="fa fa-play"></i>
</button>
<a href="{% url 'integration:api_endpoint_update' endpoint.pk %}" class="btn btn-outline-warning btn-sm" title="Edit">
<i class="fa fa-edit"></i>
</a>
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="8" class="text-center text-muted py-4">
<i class="fa fa-plug fa-2x mb-2"></i>
<div>No API endpoints found</div>
<a href="{% url 'integration:api_endpoint_create' %}" class="btn btn-primary mt-2">
<i class="fa fa-plus me-2"></i>Add First Endpoint
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Bulk Actions -->
<div id="bulk-actions" class="mt-3" style="display: none;">
<div class="alert alert-info">
<div class="d-flex justify-content-between align-items-center">
<div>
<strong><span id="selected-count">0</span></strong> endpoint(s) selected
</div>
<div>
<button class="btn btn-sm btn-success me-2" onclick="bulkTest()">
<i class="fa fa-play me-1"></i>Test Selected
</button>
<button class="btn btn-sm btn-warning me-2" onclick="bulkActivate()">
<i class="fa fa-check me-1"></i>Activate
</button>
<button class="btn btn-sm btn-secondary me-2" onclick="bulkDeactivate()">
<i class="fa fa-pause me-1"></i>Deactivate
</button>
<button class="btn btn-sm btn-secondary" onclick="clearSelection()">
<i class="fa fa-times me-1"></i>Clear Selection
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- END panel -->
<!-- Bulk Actions Modal -->
<div class="modal fade" id="bulk-actions-modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Bulk Actions</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="d-grid gap-2">
<button class="btn btn-success" onclick="testAllEndpoints()">
<i class="fa fa-play me-2"></i>Test All Endpoints
</button>
<button class="btn btn-warning" onclick="activateAllEndpoints()">
<i class="fa fa-check me-2"></i>Activate All Endpoints
</button>
<button class="btn btn-secondary" onclick="deactivateAllEndpoints()">
<i class="fa fa-pause me-2"></i>Deactivate All Endpoints
</button>
<hr>
<button class="btn btn-info" onclick="exportEndpoints()">
<i class="fa fa-download me-2"></i>Export Configuration
</button>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#import-modal">
<i class="fa fa-upload me-2"></i>Import Configuration
</button>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script src="{% static 'assets/plugins/datatables.net/js/jquery.dataTables.min.js' %}"></script>
<script src="{% static 'assets/plugins/datatables.net-bs5/js/dataTables.bootstrap5.min.js' %}"></script>
<script src="{% static 'assets/plugins/datatables.net-responsive/js/dataTables.responsive.min.js' %}"></script>
<script src="{% static 'assets/plugins/datatables.net-responsive-bs5/js/responsive.bootstrap5.min.js' %}"></script>
<script>
$(document).ready(function() {
// Initialize DataTable
var table = $('#endpoints-table').DataTable({
responsive: true,
pageLength: 25,
order: [[1, 'asc']], // Sort by name
columnDefs: [
{ orderable: false, targets: [0, 7] } // Disable sorting for checkbox and actions columns
]
});
// Select all checkbox
$('#select-all').on('change', function() {
$('.row-checkbox').prop('checked', this.checked);
updateBulkActions();
});
// Individual row checkboxes
$(document).on('change', '.row-checkbox', function() {
updateBulkActions();
// Update select all checkbox
var totalCheckboxes = $('.row-checkbox').length;
var checkedCheckboxes = $('.row-checkbox:checked').length;
$('#select-all').prop('checked', totalCheckboxes === checkedCheckboxes);
});
function updateBulkActions() {
var selectedCount = $('.row-checkbox:checked').length;
$('#selected-count').text(selectedCount);
if (selectedCount > 0) {
$('#bulk-actions').show();
} else {
$('#bulk-actions').hide();
}
}
});
function filterByStatus(status) {
$('.btn-group button').removeClass('active');
event.target.classList.add('active');
if (status === 'all') {
$('tr[data-status]').show();
} else {
$('tr[data-status]').hide();
$(`tr[data-status="${status}"]`).show();
}
}
function filterByMethod(method) {
$('.btn-group button').removeClass('active');
event.target.classList.add('active');
if (method === 'all') {
$('tr[data-method]').show();
} else {
$('tr[data-method]').hide();
$(`tr[data-method="${method}"]`).show();
}
}
function filterBySystem(systemId) {
$('.btn-group button').removeClass('active');
event.target.classList.add('active');
if (systemId === 'all') {
$('tr[data-system]').show();
} else {
$('tr[data-system]').hide();
$(`tr[data-system="${systemId}"]`).show();
}
}
function testEndpoint(endpointId) {
$.ajax({
url: '{% url "integration:api_endpoint_test" 0 %}'.replace('0', endpointId),
method: 'POST',
data: {
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
beforeSend: function() {
toastr.info('Testing endpoint...');
},
success: function(response) {
if (response.success) {
toastr.success('Endpoint test successful');
location.reload();
} else {
toastr.error('Endpoint test failed: ' + response.error);
}
},
error: function() {
toastr.error('Failed to test endpoint');
}
});
}
function testAllEndpoints() {
if (confirm('Test all endpoints? This may take some time.')) {
$.ajax({
url: '{% url "integration:api_endpoint_test_all" %}',
method: 'POST',
data: {
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
beforeSend: function() {
toastr.info('Testing all endpoints...');
},
success: function(response) {
toastr.success('Endpoint testing completed');
location.reload();
},
error: function() {
toastr.error('Failed to test endpoints');
}
});
}
}
function bulkTest() {
var selectedIds = $('.row-checkbox:checked').map(function() {
return this.value;
}).get();
if (selectedIds.length === 0) {
toastr.warning('Please select endpoints to test');
return;
}
$.ajax({
url: '{% url "integration:api_endpoint_bulk_test" %}',
method: 'POST',
data: {
'endpoint_ids': selectedIds,
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
beforeSend: function() {
toastr.info('Testing selected endpoints...');
},
success: function(response) {
toastr.success('Bulk testing completed');
location.reload();
},
error: function() {
toastr.error('Failed to test selected endpoints');
}
});
}
function bulkActivate() {
var selectedIds = $('.row-checkbox:checked').map(function() {
return this.value;
}).get();
if (selectedIds.length === 0) {
toastr.warning('Please select endpoints to activate');
return;
}
$.ajax({
url: '{% url "integration:api_endpoint_bulk_activate" %}',
method: 'POST',
data: {
'endpoint_ids': selectedIds,
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
success: function(response) {
toastr.success('Selected endpoints activated');
location.reload();
},
error: function() {
toastr.error('Failed to activate selected endpoints');
}
});
}
function bulkDeactivate() {
var selectedIds = $('.row-checkbox:checked').map(function() {
return this.value;
}).get();
if (selectedIds.length === 0) {
toastr.warning('Please select endpoints to deactivate');
return;
}
$.ajax({
url: '{% url "integration:api_endpoint_bulk_deactivate" %}',
method: 'POST',
data: {
'endpoint_ids': selectedIds,
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
success: function(response) {
toastr.success('Selected endpoints deactivated');
location.reload();
},
error: function() {
toastr.error('Failed to deactivate selected endpoints');
}
});
}
function clearSelection() {
$('.row-checkbox, #select-all').prop('checked', false);
$('#bulk-actions').hide();
}
function exportEndpoints() {
window.open('{% url "integration:api_endpoint_export" %}');
}
</script>
{% endblock %}