173 lines
No EOL
6.5 KiB
HTML
173 lines
No EOL
6.5 KiB
HTML
<form hx-post="{{ action_url }}" hx-target="#content">
|
||
<div class="form-group mb-3">
|
||
<label class="form-label">App Name</label>
|
||
<input type="text" name="name" class="form-control" value="{{ app.name if app else '' }}" required>
|
||
</div>
|
||
|
||
<div class="form-group mb-3">
|
||
<label class="form-label">App Type</label>
|
||
<select id="app-type" name="app_type" class="form-select">
|
||
<option value="">-- Select Type --</option>
|
||
<option value="web">Web Application</option>
|
||
<option value="database">Database</option>
|
||
<option value="mail">Mail Server</option>
|
||
<option value="file">File Server</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group mb-3">
|
||
<label class="form-label">Server</label>
|
||
<select name="server_id" class="form-select" required>
|
||
{% for server in servers %}
|
||
<option value="{{ server.id }}" {% if app and app.server_id==server.id %}selected{% endif %}>
|
||
{{ server.hostname }} ({{ server.ip_address }})
|
||
</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group mb-3">
|
||
<label class="form-label">Documentation</label>
|
||
{% include "components/markdown_editor.html" with content=app.documentation if app else "" %}
|
||
</div>
|
||
|
||
<div class="card mb-3">
|
||
<div class="card-header">
|
||
<div class="d-flex">
|
||
<h3 class="card-title">Ports</h3>
|
||
<div class="ms-auto">
|
||
<button type="button" class="btn btn-sm btn-outline-primary" id="suggest-ports">
|
||
Suggest Ports
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="card-body">
|
||
<div id="ports-container">
|
||
{% if app and app.ports %}
|
||
{% for port in app.ports %}
|
||
<div class="port-entry mb-2 row">
|
||
<div class="col-3">
|
||
<input type="number" name="port[]" class="form-control" value="{{ port.port }}" placeholder="Port" min="1"
|
||
max="65535">
|
||
</div>
|
||
<div class="col-3">
|
||
<select name="port_type[]" class="form-select">
|
||
<option value="tcp" {% if port.type=='tcp' %}selected{% endif %}>TCP</option>
|
||
<option value="udp" {% if port.type=='udp' %}selected{% endif %}>UDP</option>
|
||
</select>
|
||
</div>
|
||
<div class="col-5">
|
||
<input type="text" name="port_desc[]" class="form-control" value="{{ port.desc }}"
|
||
placeholder="Description">
|
||
</div>
|
||
<div class="col-1">
|
||
<button type="button" class="btn btn-sm btn-danger remove-port">×</button>
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
{% else %}
|
||
<div class="port-entry mb-2 row">
|
||
<div class="col-3">
|
||
<input type="number" name="port[]" class="form-control" placeholder="Port" min="1" max="65535">
|
||
</div>
|
||
<div class="col-3">
|
||
<select name="port_type[]" class="form-select">
|
||
<option value="tcp">TCP</option>
|
||
<option value="udp">UDP</option>
|
||
</select>
|
||
</div>
|
||
<div class="col-5">
|
||
<input type="text" name="port_desc[]" class="form-control" placeholder="Description">
|
||
</div>
|
||
<div class="col-1">
|
||
<button type="button" class="btn btn-sm btn-danger remove-port">×</button>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
<button type="button" class="btn btn-sm btn-outline-secondary mt-2" id="add-port">
|
||
Add Port
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<button type="submit" class="btn btn-primary">Save</button>
|
||
<a href="{{ cancel_url }}" class="btn btn-outline-secondary">Cancel</a>
|
||
</div>
|
||
</form>
|
||
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function () {
|
||
// Add new port entry
|
||
document.getElementById('add-port').addEventListener('click', function () {
|
||
const template = document.querySelector('.port-entry').cloneNode(true);
|
||
const inputs = template.querySelectorAll('input, select');
|
||
inputs.forEach(input => input.value = '');
|
||
if (inputs[0].type === 'number') inputs[0].value = '';
|
||
|
||
document.getElementById('ports-container').appendChild(template);
|
||
|
||
// Re-attach remove handlers
|
||
attachRemoveHandlers();
|
||
});
|
||
|
||
// Remove port entry
|
||
function attachRemoveHandlers() {
|
||
document.querySelectorAll('.remove-port').forEach(button => {
|
||
button.onclick = function () {
|
||
const portEntries = document.querySelectorAll('.port-entry');
|
||
if (portEntries.length > 1) {
|
||
this.closest('.port-entry').remove();
|
||
} else {
|
||
const inputs = this.closest('.port-entry').querySelectorAll('input, select');
|
||
inputs.forEach(input => input.value = '');
|
||
}
|
||
};
|
||
});
|
||
}
|
||
|
||
attachRemoveHandlers();
|
||
|
||
// Port suggestions based on app type
|
||
document.getElementById('suggest-ports').addEventListener('click', function () {
|
||
const appType = document.getElementById('app-type').value;
|
||
|
||
fetch(`/api/ports/suggest?type=${appType}`)
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
// Clear existing ports
|
||
const portsContainer = document.getElementById('ports-container');
|
||
portsContainer.innerHTML = '';
|
||
|
||
// Add suggested ports
|
||
data.forEach(port => {
|
||
const template = document.createElement('div');
|
||
template.className = 'port-entry mb-2 row';
|
||
template.innerHTML = `
|
||
<div class="col-3">
|
||
<input type="number" name="port[]" class="form-control" value="${port.port}" min="1" max="65535">
|
||
</div>
|
||
<div class="col-3">
|
||
<select name="port_type[]" class="form-select">
|
||
<option value="tcp" ${port.type === 'tcp' ? 'selected' : ''}>TCP</option>
|
||
<option value="udp" ${port.type === 'udp' ? 'selected' : ''}>UDP</option>
|
||
</select>
|
||
</div>
|
||
<div class="col-5">
|
||
<input type="text" name="port_desc[]" class="form-control" value="${port.desc}" placeholder="Description">
|
||
</div>
|
||
<div class="col-1">
|
||
<button type="button" class="btn btn-sm btn-danger remove-port">×</button>
|
||
</div>
|
||
`;
|
||
portsContainer.appendChild(template);
|
||
});
|
||
|
||
// Re-attach remove handlers
|
||
attachRemoveHandlers();
|
||
});
|
||
});
|
||
});
|
||
</script> |