537 lines
25 KiB
HTML
537 lines
25 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}{% if object %}Edit{% else %}Add{% endif %} API Endpoint - Integration{% 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"><a href="{% url 'integration:api_endpoint_list' %}">API Endpoints</a></li>
|
|
<li class="breadcrumb-item active">{% if object %}Edit{% else %}Add{% endif %} Endpoint</li>
|
|
</ol>
|
|
<!-- END breadcrumb -->
|
|
|
|
<!-- BEGIN page-header -->
|
|
<h1 class="page-header">
|
|
{% if object %}Edit API Endpoint{% else %}Add API Endpoint{% endif %}
|
|
<small>{% if object %}{{ object.name }}{% else %}External System Integration{% endif %}</small>
|
|
</h1>
|
|
<!-- END page-header -->
|
|
|
|
<div class="row">
|
|
<div class="col-xl-8">
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Endpoint Configuration</h4>
|
|
<div class="panel-heading-btn">
|
|
<button type="button" class="btn btn-xs btn-info me-2" onclick="testConnection()">
|
|
<i class="fa fa-play"></i> Test
|
|
</button>
|
|
<button type="button" class="btn btn-xs btn-secondary me-2" onclick="saveDraft()">
|
|
<i class="fa fa-save"></i> Save Draft
|
|
</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">
|
|
<form method="post" id="endpoint-form">
|
|
{% csrf_token %}
|
|
|
|
<!-- Basic Information -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-6">
|
|
<div class="form-floating mb-3">
|
|
{{ form.name }}
|
|
<label for="{{ form.name.id_for_label }}">Endpoint Name <span class="text-danger">*</span></label>
|
|
{% if form.name.help_text %}
|
|
<div class="form-text">{{ form.name.help_text }}</div>
|
|
{% endif %}
|
|
{% if form.name.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.name.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-floating mb-3">
|
|
{{ form.external_system }}
|
|
<label for="{{ form.external_system.id_for_label }}">External System <span class="text-danger">*</span></label>
|
|
{% if form.external_system.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.external_system.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Description</label>
|
|
{{ form.description }}
|
|
{% if form.description.help_text %}
|
|
<div class="form-text">{{ form.description.help_text }}</div>
|
|
{% endif %}
|
|
{% if form.description.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.description.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Request Configuration -->
|
|
<h6 class="border-bottom pb-2 mb-3">Request Configuration</h6>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-md-8">
|
|
<div class="form-floating">
|
|
{{ form.url }}
|
|
<label for="{{ form.url.id_for_label }}">Endpoint URL <span class="text-danger">*</span></label>
|
|
{% if form.url.help_text %}
|
|
<div class="form-text">{{ form.url.help_text }}</div>
|
|
{% endif %}
|
|
{% if form.url.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.url.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-floating">
|
|
{{ form.method }}
|
|
<label for="{{ form.method.id_for_label }}">HTTP Method <span class="text-danger">*</span></label>
|
|
{% if form.method.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.method.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-md-4">
|
|
<div class="form-floating">
|
|
{{ form.timeout }}
|
|
<label for="{{ form.timeout.id_for_label }}">Timeout (seconds)</label>
|
|
{% if form.timeout.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.timeout.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-floating">
|
|
{{ form.retry_count }}
|
|
<label for="{{ form.retry_count.id_for_label }}">Retry Count</label>
|
|
{% if form.retry_count.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.retry_count.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-floating">
|
|
{{ form.content_type }}
|
|
<label for="{{ form.content_type.id_for_label }}">Content Type</label>
|
|
{% if form.content_type.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.content_type.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Authentication -->
|
|
<h6 class="border-bottom pb-2 mb-3">Authentication</h6>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<div class="form-floating">
|
|
{{ form.auth_type }}
|
|
<label for="{{ form.auth_type.id_for_label }}">Authentication Type</label>
|
|
{% if form.auth_type.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.auth_type.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6" id="auth-details" style="display: none;">
|
|
<div class="form-floating">
|
|
{{ form.auth_credentials }}
|
|
<label for="{{ form.auth_credentials.id_for_label }}">Credentials</label>
|
|
<div class="form-text">Enter authentication credentials (will be encrypted)</div>
|
|
{% if form.auth_credentials.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.auth_credentials.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Headers -->
|
|
<h6 class="border-bottom pb-2 mb-3">Headers</h6>
|
|
|
|
<div class="mb-3">
|
|
<div id="headers-container">
|
|
<div class="row header-row mb-2">
|
|
<div class="col-md-5">
|
|
<input type="text" class="form-control" placeholder="Header name" name="header_names[]">
|
|
</div>
|
|
<div class="col-md-5">
|
|
<input type="text" class="form-control" placeholder="Header value" name="header_values[]">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<button type="button" class="btn btn-outline-danger btn-sm" onclick="removeHeader(this)">
|
|
<i class="fa fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-outline-primary btn-sm" onclick="addHeader()">
|
|
<i class="fa fa-plus me-2"></i>Add Header
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Parameters -->
|
|
<h6 class="border-bottom pb-2 mb-3">Parameters</h6>
|
|
|
|
<div class="mb-3">
|
|
<div id="parameters-container">
|
|
<div class="row parameter-row mb-2">
|
|
<div class="col-md-3">
|
|
<input type="text" class="form-control" placeholder="Parameter name" name="param_names[]">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<select class="form-select" name="param_types[]">
|
|
<option value="string">String</option>
|
|
<option value="integer">Integer</option>
|
|
<option value="boolean">Boolean</option>
|
|
<option value="date">Date</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<div class="form-check mt-2">
|
|
<input class="form-check-input" type="checkbox" name="param_required[]">
|
|
<label class="form-check-label">Required</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<input type="text" class="form-control" placeholder="Default value" name="param_defaults[]">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<button type="button" class="btn btn-outline-danger btn-sm" onclick="removeParameter(this)">
|
|
<i class="fa fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-outline-primary btn-sm" onclick="addParameter()">
|
|
<i class="fa fa-plus me-2"></i>Add Parameter
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Response Configuration -->
|
|
<h6 class="border-bottom pb-2 mb-3">Response Configuration</h6>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-md-4">
|
|
<div class="form-floating">
|
|
{{ form.expected_status_code }}
|
|
<label for="{{ form.expected_status_code.id_for_label }}">Expected Status Code</label>
|
|
{% if form.expected_status_code.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.expected_status_code.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-floating">
|
|
{{ form.response_format }}
|
|
<label for="{{ form.response_format.id_for_label }}">Response Format</label>
|
|
{% if form.response_format.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.response_format.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-floating">
|
|
{{ form.status }}
|
|
<label for="{{ form.status.id_for_label }}">Status</label>
|
|
{% if form.status.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.status.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Options -->
|
|
<h6 class="border-bottom pb-2 mb-3">Options</h6>
|
|
|
|
<div class="row mb-4">
|
|
<div class="col-md-4">
|
|
<div class="form-check">
|
|
{{ form.validate_response }}
|
|
<label class="form-check-label" for="{{ form.validate_response.id_for_label }}">
|
|
Validate Response
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-check">
|
|
{{ form.log_requests }}
|
|
<label class="form-check-label" for="{{ form.log_requests.id_for_label }}">
|
|
Log Requests
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-check">
|
|
{{ form.enable_monitoring }}
|
|
<label class="form-check-label" for="{{ form.enable_monitoring.id_for_label }}">
|
|
Enable Monitoring
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Action Buttons -->
|
|
<div class="d-flex justify-content-between">
|
|
<div>
|
|
<a href="{% url 'integration:api_endpoint_list' %}" class="btn btn-secondary">
|
|
<i class="fa fa-arrow-left me-2"></i>Cancel
|
|
</a>
|
|
</div>
|
|
<div>
|
|
<button type="button" class="btn btn-info me-2" onclick="testConnection()">
|
|
<i class="fa fa-play me-2"></i>Test Connection
|
|
</button>
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fa fa-save me-2"></i>{% if object %}Update{% else %}Create{% endif %} Endpoint
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
</div>
|
|
|
|
<div class="col-xl-4">
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Help & Tips</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="alert alert-info">
|
|
<h6 class="alert-heading">Configuration Tips</h6>
|
|
<ul class="mb-0 small">
|
|
<li>Use descriptive names for easy identification</li>
|
|
<li>Test the endpoint before saving</li>
|
|
<li>Set appropriate timeout values</li>
|
|
<li>Enable logging for debugging</li>
|
|
<li>Use authentication when required</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="card border-secondary mb-3">
|
|
<div class="card-header">
|
|
<h6 class="card-title mb-0">URL Variables</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="card-text small">You can use these variables in your URL:</p>
|
|
<ul class="small mb-0">
|
|
<li><code>{base_url}</code> - System base URL</li>
|
|
<li><code>{api_version}</code> - API version</li>
|
|
<li><code>{tenant_id}</code> - Current tenant ID</li>
|
|
<li><code>{user_id}</code> - Current user ID</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card border-warning">
|
|
<div class="card-header">
|
|
<h6 class="card-title mb-0">Security Notes</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="card-text small">Important security considerations:</p>
|
|
<ul class="small mb-0">
|
|
<li>Credentials are encrypted at rest</li>
|
|
<li>Use HTTPS for sensitive data</li>
|
|
<li>Validate all responses</li>
|
|
<li>Monitor for unusual activity</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
|
|
{% if object %}
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Current Status</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<table class="table table-borderless table-sm">
|
|
<tr>
|
|
<td class="fw-bold" width="100">Status:</td>
|
|
<td>
|
|
<span class="badge bg-{% if object.status == 'ACTIVE' %}success{% elif object.status == 'INACTIVE' %}secondary{% else %}warning{% endif %}">
|
|
{{ object.get_status_display }}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Health:</td>
|
|
<td>
|
|
{% if object.health_status %}
|
|
<span class="badge bg-{% if object.health_status == 'HEALTHY' %}success{% elif object.health_status == 'WARNING' %}warning{% else %}danger{% endif %}">
|
|
{{ object.health_status }}
|
|
</span>
|
|
{% else %}
|
|
<span class="text-muted">Not tested</span>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Last Test:</td>
|
|
<td>{{ object.last_tested|date:"M d, H:i"|default:"Never" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Success Rate:</td>
|
|
<td>{{ object.success_rate|floatformat:1 }}%</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<script>
|
|
$(document).ready(function() {
|
|
// Show/hide auth details based on auth type
|
|
$('#id_auth_type').on('change', function() {
|
|
if ($(this).val() && $(this).val() !== 'NONE') {
|
|
$('#auth-details').show();
|
|
} else {
|
|
$('#auth-details').hide();
|
|
}
|
|
}).trigger('change');
|
|
|
|
// Auto-save draft functionality
|
|
setInterval(function() {
|
|
saveDraft(true); // Silent save
|
|
}, 30000); // Every 30 seconds
|
|
});
|
|
|
|
function addHeader() {
|
|
var headerRow = `
|
|
<div class="row header-row mb-2">
|
|
<div class="col-md-5">
|
|
<input type="text" class="form-control" placeholder="Header name" name="header_names[]">
|
|
</div>
|
|
<div class="col-md-5">
|
|
<input type="text" class="form-control" placeholder="Header value" name="header_values[]">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<button type="button" class="btn btn-outline-danger btn-sm" onclick="removeHeader(this)">
|
|
<i class="fa fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
$('#headers-container').append(headerRow);
|
|
}
|
|
|
|
function removeHeader(button) {
|
|
$(button).closest('.header-row').remove();
|
|
}
|
|
|
|
function addParameter() {
|
|
var paramRow = `
|
|
<div class="row parameter-row mb-2">
|
|
<div class="col-md-3">
|
|
<input type="text" class="form-control" placeholder="Parameter name" name="param_names[]">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<select class="form-select" name="param_types[]">
|
|
<option value="string">String</option>
|
|
<option value="integer">Integer</option>
|
|
<option value="boolean">Boolean</option>
|
|
<option value="date">Date</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<div class="form-check mt-2">
|
|
<input class="form-check-input" type="checkbox" name="param_required[]">
|
|
<label class="form-check-label">Required</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<input type="text" class="form-control" placeholder="Default value" name="param_defaults[]">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<button type="button" class="btn btn-outline-danger btn-sm" onclick="removeParameter(this)">
|
|
<i class="fa fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
$('#parameters-container').append(paramRow);
|
|
}
|
|
|
|
function removeParameter(button) {
|
|
$(button).closest('.parameter-row').remove();
|
|
}
|
|
|
|
function testConnection() {
|
|
var formData = new FormData($('#endpoint-form')[0]);
|
|
|
|
$.ajax({
|
|
url: '{% url "integration:api_endpoint_test_config" %}',
|
|
method: 'POST',
|
|
data: formData,
|
|
processData: false,
|
|
contentType: false,
|
|
beforeSend: function() {
|
|
toastr.info('Testing connection...');
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
toastr.success('Connection test successful');
|
|
} else {
|
|
toastr.error('Connection test failed: ' + response.error);
|
|
}
|
|
},
|
|
error: function() {
|
|
toastr.error('Failed to test connection');
|
|
}
|
|
});
|
|
}
|
|
|
|
function saveDraft(silent = false) {
|
|
var formData = new FormData($('#endpoint-form')[0]);
|
|
formData.append('save_draft', 'true');
|
|
|
|
$.ajax({
|
|
url: window.location.href,
|
|
method: 'POST',
|
|
data: formData,
|
|
processData: false,
|
|
contentType: false,
|
|
beforeSend: function() {
|
|
if (!silent) {
|
|
toastr.info('Saving draft...');
|
|
}
|
|
},
|
|
success: function(response) {
|
|
if (!silent) {
|
|
toastr.success('Draft saved successfully');
|
|
}
|
|
},
|
|
error: function() {
|
|
if (!silent) {
|
|
toastr.error('Failed to save draft');
|
|
}
|
|
}
|
|
});
|
|
}
|
|
</script>
|
|
{% endblock %}
|
|
|