290 lines
No EOL
11 KiB
HTML
290 lines
No EOL
11 KiB
HTML
{% extends "layout.html" %}
|
|
|
|
{% block content %}
|
|
<div class="container-xl">
|
|
<div class="page-header d-print-none">
|
|
<div class="row align-items-center">
|
|
<div class="col">
|
|
<h2 class="page-title">
|
|
{{ title }}
|
|
</h2>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mt-3">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<form method="POST">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
|
<div class="mb-3">
|
|
<label for="hostname" class="form-label">Hostname</label>
|
|
<input type="text" class="form-control" id="hostname" name="hostname" required
|
|
value="{{ server.hostname if server else '' }}">
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="ip_address" class="form-label">IP Address</label>
|
|
<input type="text" class="form-control" id="ip_address" name="ip_address" required
|
|
value="{{ server.ip_address if server else '' }}">
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<label for="subnet_id" class="form-label">Subnet (optional for public IPs)</label>
|
|
<select class="form-select" id="subnet_id" name="subnet_id">
|
|
<option value="">No subnet (standalone server)</option>
|
|
{% for subnet in subnets %}
|
|
<option value="{{ subnet.id }}" {% if server and server.subnet_id==subnet.id %}selected{% endif %}>
|
|
{{ subnet.cidr }} ({{ subnet.location_ref.name }})
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="ms-2 pt-4">
|
|
<button type="button" class="btn btn-outline-primary btn-icon" data-bs-toggle="modal"
|
|
data-bs-target="#add-subnet-modal">
|
|
<span class="ti ti-plus"></span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<label for="location_id" class="form-label">Location (required for standalone servers)</label>
|
|
<select class="form-select" id="location_id" name="location_id">
|
|
<option value="">Select a location</option>
|
|
{% for location in locations %}
|
|
<option value="{{ location.id }}" {% if server and server.location_id==location.id %}selected{%
|
|
endif %}>
|
|
{{ location.name }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="ms-2 pt-4">
|
|
<button type="button" class="btn btn-outline-primary btn-icon" data-bs-toggle="modal"
|
|
data-bs-target="#add-location-modal">
|
|
<span class="ti ti-plus"></span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="description" class="form-label">Description (optional)</label>
|
|
<textarea class="form-control" id="description" name="description"
|
|
rows="3">{{ server.description if server else '' }}</textarea>
|
|
</div>
|
|
|
|
<div class="form-footer">
|
|
<button type="submit" class="btn btn-primary">Save</button>
|
|
<a href="{{ url_for('dashboard.server_list') }}" class="btn btn-link">Cancel</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Add Subnet Modal -->
|
|
<div class="modal modal-blur fade" id="add-subnet-modal" tabindex="-1" role="dialog" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Add New Subnet</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="add-subnet-form">
|
|
<div class="row mb-3">
|
|
<div class="col-md-8">
|
|
<label class="form-label required">IP Address</label>
|
|
<input type="text" class="form-control" id="new-subnet-ip" placeholder="192.168.1.0" required>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label required">Prefix</label>
|
|
<select class="form-select" id="new-subnet-prefix" required>
|
|
{% for i in range(8, 31) %}
|
|
<option value="{{ i }}">{{ i }} ({{ 2**(32-i) }} hosts)</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label required">Location</label>
|
|
<select class="form-select" id="new-subnet-location" required>
|
|
<option value="">Select a location</option>
|
|
{% for location in locations %}
|
|
<option value="{{ location.id }}">{{ location.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="new-subnet-auto-scan">
|
|
<label class="form-check-label" for="new-subnet-auto-scan">
|
|
Auto-scan for active hosts
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-link link-secondary" data-bs-dismiss="modal">
|
|
Cancel
|
|
</button>
|
|
<button type="button" class="btn btn-primary ms-auto" id="save-subnet-btn">
|
|
<span class="ti ti-plus me-2"></span>
|
|
Add Subnet
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Add Location Modal -->
|
|
<div class="modal modal-blur fade" id="add-location-modal" tabindex="-1" role="dialog" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Add New Location</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="add-location-form">
|
|
<div class="mb-3">
|
|
<label class="form-label required">Name</label>
|
|
<input type="text" class="form-control" id="new-location-name" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Description (optional)</label>
|
|
<textarea class="form-control" id="new-location-description" rows="3"></textarea>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-link link-secondary" data-bs-dismiss="modal">
|
|
Cancel
|
|
</button>
|
|
<button type="button" class="btn btn-primary ms-auto" id="save-location-btn">
|
|
<span class="ti ti-plus me-2"></span>
|
|
Add Location
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
const subnetSelect = document.getElementById('subnet_id');
|
|
const locationField = document.querySelector('.mb-3:has(#location_id)');
|
|
|
|
function updateLocationVisibility() {
|
|
if (subnetSelect.value === '') {
|
|
locationField.style.display = 'block';
|
|
document.getElementById('location_id').setAttribute('required', 'required');
|
|
} else {
|
|
locationField.style.display = 'none';
|
|
document.getElementById('location_id').removeAttribute('required');
|
|
}
|
|
}
|
|
|
|
// Initial state
|
|
if (locationField) {
|
|
updateLocationVisibility();
|
|
// Update on change
|
|
subnetSelect.addEventListener('change', updateLocationVisibility);
|
|
}
|
|
|
|
// Add subnet functionality
|
|
const saveSubnetBtn = document.getElementById('save-subnet-btn');
|
|
const addSubnetModal = document.getElementById('add-subnet-modal');
|
|
|
|
if (saveSubnetBtn && addSubnetModal) {
|
|
const bsSubnetModal = new bootstrap.Modal(addSubnetModal);
|
|
|
|
saveSubnetBtn.addEventListener('click', function () {
|
|
const ip = document.getElementById('new-subnet-ip').value.trim();
|
|
const prefix = document.getElementById('new-subnet-prefix').value;
|
|
const locationId = document.getElementById('new-subnet-location').value;
|
|
const autoScan = document.getElementById('new-subnet-auto-scan').checked;
|
|
const csrfToken = document.querySelector('input[name="csrf_token"]').value;
|
|
|
|
if (!ip || !prefix || !locationId) {
|
|
alert('All fields are required');
|
|
return;
|
|
}
|
|
|
|
apiFunctions.createSubnet(`${ip}/${prefix}`, locationId, autoScan, csrfToken)
|
|
.then(data => {
|
|
// Add new option to select dropdown
|
|
const locationName = document.querySelector(`#new-subnet-location option[value="${locationId}"]`).textContent;
|
|
const newOption = new Option(`${data.cidr} (${locationName})`, data.id, true, true);
|
|
subnetSelect.add(newOption);
|
|
|
|
// Reset form and close modal
|
|
document.getElementById('new-subnet-ip').value = '';
|
|
document.getElementById('new-subnet-prefix').value = '24';
|
|
document.getElementById('new-subnet-location').value = '';
|
|
document.getElementById('new-subnet-auto-scan').checked = false;
|
|
bsSubnetModal.hide();
|
|
|
|
// Trigger the subnet change event to hide location if needed
|
|
const event = new Event('change');
|
|
subnetSelect.dispatchEvent(event);
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
alert('Failed to create subnet: ' + error.message);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Add location functionality
|
|
const saveLocationBtn = document.getElementById('save-location-btn');
|
|
const locationSelect = document.getElementById('location_id');
|
|
const addLocationModal = document.getElementById('add-location-modal');
|
|
|
|
if (saveLocationBtn && locationSelect && addLocationModal) {
|
|
const bsLocationModal = new bootstrap.Modal(addLocationModal);
|
|
|
|
saveLocationBtn.addEventListener('click', function () {
|
|
const name = document.getElementById('new-location-name').value.trim();
|
|
const description = document.getElementById('new-location-description').value.trim();
|
|
const csrfToken = document.querySelector('input[name="csrf_token"]').value;
|
|
|
|
if (!name) {
|
|
alert('Location name is required');
|
|
return;
|
|
}
|
|
|
|
apiFunctions.createLocation(name, description, csrfToken)
|
|
.then(data => {
|
|
// Add new option to select dropdown
|
|
const newOption = new Option(data.name, data.id, true, true);
|
|
locationSelect.add(newOption);
|
|
|
|
// Reset form and close modal
|
|
document.getElementById('new-location-name').value = '';
|
|
document.getElementById('new-location-description').value = '';
|
|
bsLocationModal.hide();
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
alert('Failed to create location: ' + error.message);
|
|
});
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %} |