homedocs/app/templates/dashboard/app_view.html
2025-03-31 01:03:02 +02:00

404 lines
No EOL
13 KiB
HTML

{% extends "layout.html" %}
{% block title %}{{ title }}{% endblock %}
{% block content %}
<div class="container-xl">
<div class="page-header d-print-none">
<div class="row align-items-center">
<div class="col">
<div class="page-pretitle">
Application Details
</div>
<h2 class="page-title">
{{ app.name }}
</h2>
</div>
<div class="col-auto ms-auto d-print-none">
<div class="btn-list">
<a href="{{ url_for('dashboard.app_edit', app_id=app.id) }}" class="btn btn-primary d-none d-sm-inline-block">
<span class="ti ti-edit"></span>
Edit Application
</a>
<button class="btn btn-danger d-none d-sm-inline-block" data-bs-toggle="modal"
data-bs-target="#deleteAppModal">
<span class="ti ti-trash"></span>
Delete
</button>
</div>
</div>
</div>
</div>
<div class="row g-3">
<div class="col-md-4">
<!-- Basic Information card -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Basic Information</h3>
</div>
<div class="card-body">
<div class="mb-3">
<div class="form-label">Server</div>
<div><a href="{{ url_for('dashboard.server_view', server_id=app.server.id) }}">{{ app.server.name }}</a> ({{
app.server.ip_address }})</div>
</div>
<div class="mb-3">
<div class="form-label">Created</div>
<div>{{ app.created_at }}</div>
</div>
<div class="mb-3">
<div class="form-label">Last Updated</div>
<div>{{ app.updated_at }}</div>
</div>
</div>
</div>
<!-- Ports card -->
<div class="card mt-3">
<div class="card-header d-flex justify-content-between align-items-center">
<h3 class="card-title">Ports</h3>
<button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#addPortModal">
<span class="ti ti-plus"></span> Add Port
</button>
</div>
<div class="card-body p-0">
{% if app.ports %}
<div class="table-responsive">
<table class="table table-vcenter card-table">
<thead>
<tr>
<th>PORT</th>
<th>PROTOCOL</th>
<th>DESCRIPTION</th>
<th class="w-1"></th>
</tr>
</thead>
<tbody>
{% for port in app.ports %}
<tr>
<td>{{ port.port_number }}</td>
<td><span class="badge bg-blue">{{ port.protocol }}</span></td>
<td>{{ port.description }}</td>
<td>
<button type="button" class="btn btn-sm btn-ghost-danger"
onclick="confirmDeletePort({{ port.id }})">
<span class="ti ti-trash"></span>
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="empty p-4">
<div class="empty-icon">
<span class="ti ti-plug"></span>
</div>
<p class="empty-title">No ports configured</p>
<p class="empty-subtitle text-muted">Add ports to document what this application uses</p>
<div class="empty-action">
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addPortModal">
<span class="ti ti-plus me-1"></span> Add Port
</button>
</div>
</div>
{% endif %}
</div>
</div>
</div>
<div class="col-md-8">
<!-- Documentation section -->
<div class="card h-100">
<div class="card-header">
<h3 class="card-title">Documentation</h3>
</div>
<div class="card-body markdown-content p-4">
{% if app.documentation %}
{{ app.documentation|markdown|safe }}
{% else %}
<div class="empty">
<div class="empty-icon">
<span class="ti ti-file-text"></span>
</div>
<p class="empty-title">No documentation available</p>
<p class="empty-subtitle text-muted">
Add documentation to this application to keep track of important information.
</p>
<div class="empty-action">
<a href="{{ url_for('dashboard.app_edit', app_id=app.id) }}" class="btn btn-primary">
<span class="ti ti-edit me-2"></span> Add Documentation
</a>
</div>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<!-- Add Port Modal -->
<div class="modal modal-blur fade" id="addPortModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Add Port</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form id="addPortForm" action="{{ url_for('api.add_app_port', app_id=app.id) }}" method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div class="modal-body">
<div class="mb-3">
<label class="form-label required">Port Number</label>
<div class="input-group">
<input type="number" class="form-control" name="port_number" min="1" max="65535" required>
<button class="btn btn-outline-secondary" type="button" id="randomPortBtn">
<span class="ti ti-dice"></span>
</button>
</div>
</div>
<div class="mb-3">
<label class="form-label">Protocol</label>
<select class="form-select" name="protocol">
<option value="TCP">TCP</option>
<option value="UDP">UDP</option>
<option value="SCTP">SCTP</option>
<option value="OTHER">OTHER</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">Description</label>
<input type="text" class="form-control" name="description" placeholder="Optional description">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-link link-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary ms-auto">Add Port</button>
</div>
</form>
</div>
</div>
</div>
<!-- Delete Port Modal -->
<div class="modal modal-blur fade" id="deletePortModal" tabindex="-1">
<div class="modal-dialog modal-sm modal-dialog-centered">
<div class="modal-content">
<div class="modal-body">
<div class="modal-title">Are you sure?</div>
<div>This will permanently delete this port.</div>
</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-danger" id="confirmDeleteBtn">Delete Port</button>
</div>
</div>
</div>
</div>
<!-- Delete App Modal -->
<div class="modal modal-blur fade" id="deleteAppModal" tabindex="-1">
<div class="modal-dialog modal-sm modal-dialog-centered">
<div class="modal-content">
<div class="modal-body">
<div class="modal-title">Are you sure?</div>
<div>This will permanently delete the application "{{ app.name }}" and all its ports.</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<form method="POST" action="{{ url_for('dashboard.app_delete', app_id=app.id) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="btn btn-danger">Delete Application</button>
</form>
</div>
</div>
</div>
</div>
<!-- Add some additional CSS for better markdown rendering -->
<style>
.markdown-content {
line-height: 1.6;
padding: 1.5rem !important;
}
.markdown-content h1,
.markdown-content h2,
.markdown-content h3,
.markdown-content h4,
.markdown-content h5,
.markdown-content h6 {
margin-top: 1.5rem;
margin-bottom: 1rem;
font-weight: 600;
}
.markdown-content h1 {
font-size: 1.8rem;
padding-bottom: 0.3rem;
border-bottom: 1px solid var(--tblr-border-color);
}
.markdown-content h2 {
font-size: 1.5rem;
padding-bottom: 0.3rem;
border-bottom: 1px solid var(--tblr-border-color);
}
.markdown-content p {
margin-bottom: 1rem;
}
.markdown-content ul,
.markdown-content ol {
margin-bottom: 1rem;
padding-left: 2rem;
}
.markdown-content blockquote {
padding: 0.5rem 1rem;
margin-bottom: 1rem;
border-left: 0.25rem solid var(--tblr-border-color);
color: var(--tblr-secondary);
}
.markdown-content pre {
padding: 1rem;
margin-bottom: 1rem;
background-color: var(--tblr-bg-surface);
border-radius: 0.25rem;
overflow-x: auto;
}
.markdown-content code {
padding: 0.2rem 0.4rem;
border-radius: 0.25rem;
background-color: var(--tblr-bg-surface);
font-size: 0.875em;
}
.markdown-content pre code {
padding: 0;
background-color: transparent;
}
.markdown-content img {
max-width: 100%;
border-radius: 0.25rem;
margin: 1rem 0;
}
.markdown-content table {
width: 100%;
margin-bottom: 1rem;
border-collapse: collapse;
}
.markdown-content table th,
.markdown-content table td {
padding: 0.5rem;
border: 1px solid var(--tblr-border-color);
}
.markdown-content table th {
background-color: var(--tblr-bg-surface);
font-weight: 600;
}
/* GitHub-style alerts styling */
.markdown-content blockquote:has(p:first-child:contains("[!NOTE]")),
.markdown-content blockquote:has(p:first-child:contains("[!TIP]")),
.markdown-content blockquote:has(p:first-child:contains("[!IMPORTANT]")),
.markdown-content blockquote:has(p:first-child:contains("[!WARNING]")),
.markdown-content blockquote:has(p:first-child:contains("[!CAUTION]")) {
padding: 1rem;
margin: 1rem 0;
border-left-width: 0.25rem;
border-radius: 0.25rem;
}
.markdown-content blockquote:has(p:first-child:contains("[!NOTE]")) {
background-color: rgba(0, 120, 215, 0.1);
border-left-color: #0078d7;
}
.markdown-content blockquote:has(p:first-child:contains("[!TIP]")) {
background-color: rgba(46, 160, 67, 0.1);
border-left-color: #2ea043;
}
.markdown-content blockquote:has(p:first-child:contains("[!IMPORTANT]")) {
background-color: rgba(162, 80, 214, 0.1);
border-left-color: #a250d6;
}
.markdown-content blockquote:has(p:first-child:contains("[!WARNING]")) {
background-color: rgba(245, 159, 0, 0.1);
border-left-color: #f59f00;
}
.markdown-content blockquote:has(p:first-child:contains("[!CAUTION]")) {
background-color: rgba(215, 58, 73, 0.1);
border-left-color: #d73a49;
}
</style>
{% endblock %}
{% block extra_js %}
<script>
// Store app ID for JavaScript use
const appId = {{ app.id }};
let portToDelete = null;
// IMPORTANT: Define confirmDeletePort outside the DOMContentLoaded event
// so it's available in the global scope
function confirmDeletePort(portId) {
portToDelete = portId;
const modal = new bootstrap.Modal(document.getElementById('deletePortModal'));
modal.show();
}
// Set up event listeners when DOM is ready
document.addEventListener('DOMContentLoaded', function () {
// Handle random port button click
const randomPortBtn = document.getElementById('randomPortBtn');
if (randomPortBtn) {
randomPortBtn.addEventListener('click', function () {
fetch('/api/ports/random')
.then(response => response.json())
.then(data => {
document.querySelector('#addPortForm input[name="port_number"]').value = data.port;
})
.catch(error => {
console.error('Error fetching random port:', error);
});
});
}
// Handle port deletion confirmation
const confirmDeleteBtn = document.getElementById('confirmDeleteBtn');
if (confirmDeleteBtn) {
confirmDeleteBtn.addEventListener('click', function () {
if (portToDelete) {
// Create a form and submit it programmatically
const form = document.createElement('form');
form.method = 'POST';
form.action = `/api/app/${appId}/port/${portToDelete}/delete`;
const csrfInput = document.createElement('input');
csrfInput.type = 'hidden';
csrfInput.name = 'csrf_token';
csrfInput.value = '{{ csrf_token() }}';
form.appendChild(csrfInput);
document.body.appendChild(form);
form.submit();
}
});
}
});
</script>
{% endblock %}