peardock/index.html
2024-12-03 00:49:32 -05:00

700 lines
20 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Docker P2P Manager</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
<!-- xterm.css for Terminal -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm/css/xterm.css">
<style>
body {
margin: 0;
display: flex;
height: 100vh;
background-color: #1a1a1a;
color: white;
overflow: hidden;
}
.hidden {
display: none !important;
}
#titlebar {
-webkit-app-region: drag;
height: 30px;
width: 100%;
position: fixed;
top: 0;
background-color: #2c2c2c;
z-index: 1000;
}
pear-ctrl[data-platform="darwin"] {
float: left;
margin-top: 5px;
margin-left: 10px;
}
#sidebar {
position: fixed;
top: 30px;
left: 0;
background-color: #2c2c2c;
height: calc(100vh - 30px);
width: 250px;
overflow-y: auto;
transition: width 0.3s ease-in-out;
}
#sidebar.collapsed {
width: 50px;
}
#sidebar.collapsed .content {
display: none;
}
#collapse-sidebar-btn {
position: absolute;
top: 10px;
right: 10px;
background-color: #444;
border: none;
color: white;
width: 30px;
height: 30px;
border-radius: 50%;
font-size: 16px;
line-height: 30px;
text-align: center;
cursor: pointer;
}
#content {
display: flex;
flex-direction: column;
/* Keep vertical stacking for child elements */
margin-left: 250px;
/* Leave space for the sidebar */
flex: 1;
/* Allow the content to grow */
overflow-y: auto;
/* Allow scrolling if content overflows */
position: relative;
}
#sidebar.collapsed~#content {
margin-left: 50px;
}
.connection-status {
border-radius: 50%;
width: 10px;
height: 10px;
display: inline-block;
margin-right: 8px;
}
.status-connected {
background-color: green;
}
.status-disconnected {
background-color: red;
}
#terminal-modal {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
max-height: 90vh;
height: 300px;
background-color: #1a1a1a;
border-top: 2px solid #444;
display: none;
flex-direction: column;
z-index: 1000;
overflow: hidden;
}
#terminal-modal .header {
background-color: #444;
cursor: move;
padding: 10px;
display: flex;
justify-content: space-between;
align-items: center;
}
#terminal-resize-handle {
width: 100%;
height: 10px;
cursor: ns-resize;
background-color: #444;
position: absolute;
bottom: 0;
left: 0;
}
#terminal-container {
flex: 1;
overflow: hidden;
background-color: black;
color: white;
}
#tray {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
background-color: #444;
padding: 5px 10px;
display: flex;
gap: 10px;
overflow-x: auto;
white-space: nowrap;
z-index: 999;
}
#tray .tray-item {
background-color: #555;
color: white;
padding: 5px 10px;
border-radius: 5px;
cursor: pointer;
}
#status-indicator {
display: none;
/* Ensure it's hidden by default */
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.75);
z-index: 1050;
}
#status-indicator .spinner-border {
width: 3rem;
height: 3rem;
}
#status-indicator p {
margin-top: 1rem;
color: #fff;
font-size: 1.25rem;
}
#welcome-page {
display: flex;
flex-direction: column;
/* Stack child elements vertically */
justify-content: center;
/* Center content vertically */
align-items: center;
/* Center content horizontally */
text-align: center;
/* Center-align text */
position: absolute;
/* Overlay it over the content area */
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
/* Center it perfectly in the content */
max-width: 800px;
/* Restrict the width of the welcome page */
width: 100%;
/* Allow it to scale */
padding: 20px;
background-color: transparent;
/* Match the theme */
}
#welcome-page.hidden {
display: none !important;
/* Completely hide when not needed */
}
#dashboard {
display: flex;
/* Use flex layout for content within the dashboard */
flex-direction: column;
/* Stack elements vertically */
flex: 1;
/* Ensure it uses all available space */
width: 100%;
/* Take up the full width of the content area */
margin-top: 30px;
/* Remove extra padding */
overflow-y: auto;
/* Allow vertical scrolling if needed */
position: relative;
/* Prevent overlap with other elements */
}
#dashboard.hidden {
display: none !important;
/* Hide the dashboard completely when not needed */
}
#alert-container {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
z-index: 1055;
/* Ensure it overlays important elements only */
display: flex;
flex-direction: column-reverse;
/* Stack alerts upwards */
gap: 10px;
/* Add space between alerts */
pointer-events: none;
/* Prevent container from blocking clicks */
}
#alert-container {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
z-index: 1055;
display: flex;
flex-direction: column-reverse;
gap: 10px;
pointer-events: none;
/* Prevent container from blocking clicks */
}
.alert {
display: inline-flex;
align-items: center;
justify-content: space-between;
padding: 12px 20px;
background-color: #2b2b2b;
color: #e0e0e0;
border-radius: 6px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
font-family: Arial, sans-serif;
font-size: 14px;
animation: fadeIn 0.3s ease-out, fadeOut 4.5s ease-in forwards;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
pointer-events: auto;
/* Allow alerts to be interactive */
}
.alert.success {
border-left: 6px solid #28a745;
/* Green border for success */
}
.alert.danger {
border-left: 6px solid #dc3545;
/* Red border for danger */
}
.alert .close-btn {
background: none;
border: none;
color: #e0e0e0;
font-size: 16px;
cursor: pointer;
margin-left: auto;
/* Align to the far right */
}
@media (max-width: 768px) {
#alert-container {
bottom: 10px;
right: 10px;
}
.alert {
max-width: 90%;
font-size: 12px;
padding: 10px 15px;
white-space: normal;
/* Allow wrapping on small screens */
text-overflow: clip;
/* Disable ellipsis when wrapping */
}
}
@media (min-width: 768px) {
.alert {
white-space: nowrap;
/* Re-enable nowrap for larger screens */
text-overflow: ellipsis;
/* Add ellipsis for overflowed text */
}
}
#sidebar.collapsed .btn-danger {
display: none;
}
/* General scrollbar styles for dark and skinny scrollbars */
* {
scrollbar-width: thin;
/* Firefox */
scrollbar-color: #555 #1a1a1a;
/* Firefox: thumb and track color */
}
/* WebKit-based browsers (Chrome, Edge, Safari) */
*::-webkit-scrollbar {
width: 8px;
/* Width of vertical scrollbar */
height: 8px;
/* Height of horizontal scrollbar */
}
*::-webkit-scrollbar-track {
background: #1a1a1a;
/* Track color */
}
*::-webkit-scrollbar-thumb {
background-color: #555;
/* Thumb color */
border-radius: 10px;
/* Round the thumb */
border: 2px solid #1a1a1a;
/* Space between thumb and track */
}
*::-webkit-scrollbar-thumb:hover {
background-color: #777;
/* Lighter color on hover */
}
*::-webkit-scrollbar-thumb:active {
background-color: #999;
/* Even lighter color when active */
}
.list-group-item {
position: relative;
display: block;
padding: var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x);
color: var(--bs-list-group-color);
text-decoration: none;
background-color: #2c2c2c
;
}
.list-group-item {
position: relative;
display: block;
padding: var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x);
color: #ffffff;
text-decoration: none;
background-color: #2c2c2c;
}
.text-primary {
--bs-text-opacity: 1;
color: rgb(254 254 254) !important;
}
.list-group {
--bs-list-group-color: var(--bs-body-color);
--bs-list-group-bg: var(--bs-body-bg);
--bs-list-group-border-color: transparent;
--bs-list-group-border-width: var(--bs-border-width);
--bs-list-group-border-radius: var(--bs-border-radius);
--bs-list-group-item-padding-x: 1rem;
--bs-list-group-item-padding-y: 0.5rem;
--bs-list-group-action-color: var(--bs-secondary-color);
--bs-list-group-action-hover-color: var(--bs-emphasis-color);
--bs-list-group-action-hover-bg: var(--bs-tertiary-bg);
--bs-list-group-action-active-color: var(--bs-body-color);
--bs-list-group-action-active-bg: var(--bs-secondary-bg);
--bs-list-group-disabled-color: var(--bs-secondary-color);
--bs-list-group-disabled-bg: var(--bs-body-bg);
--bs-list-group-active-color: #fff;
--bs-list-group-active-bg: #0d6efd;
--bs-list-group-active-border-color: #0d6efd;
display: flex;
flex-direction: column;
padding-left: 0;
margin-bottom: 0;
border-radius: var(--bs-list-group-border-radius);
}
</style>
</head>
<body>
<div id="titlebar">
<pear-ctrl></pear-ctrl>
</div>
<div id="sidebar">
<button id="collapse-sidebar-btn">&lt;</button>
<div class="content">
<h4 class="text-center mt-3">Connections</h4>
<ul id="connection-list" class="list-group mb-3"></ul>
<form id="add-connection-form" class="px-3 d-flex align-items-center">
<input type="text" id="new-connection-topic" class="form-control me-2" placeholder="Enter server topic" required>
<button type="submit" class="btn btn-primary">
<i class="fas fa-plug"></i> Add
</button>
</form>
</div>
</div>
<div id="content">
<div id="welcome-page">
<h1>Welcome to peardock</h1>
<p class="mt-3">Easily manage your Docker containers across peer-to-peer connections.</p>
<p>To get started, add a connection using the form in the sidebar.</p>
<!-- <img src="https://via.placeholder.com/500x300" alt="Welcome Graphic" class="img-fluid mt-4"> -->
</div>
<div id="dashboard" class="hidden">
<div class="table-responsive">
<table class="table table-dark table-striped">
<thead>
<tr>
<th>Name</th>
<th>Image</th>
<th>Status</th>
<th>CPU (%)</th>
<th>Memory (MB)</th>
<th>IP Address</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="container-list"></tbody>
</table>
</div>
</div>
</div>
<!-- Duplicate Container Modal -->
<div class="modal fade" id="duplicateModal" tabindex="-1" aria-labelledby="duplicateModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content bg-dark text-white">
<div class="modal-header">
<h5 class="modal-title" id="duplicateModalLabel">Duplicate Container</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="duplicate-container-form">
<div class="mb-3">
<label for="container-name" class="form-label">Container Name</label>
<input type="text" class="form-control" id="container-name" required>
</div>
<div class="mb-3">
<label for="container-hostname" class="form-label">Hostname</label>
<input type="text" class="form-control" id="container-hostname" required>
</div>
<div class="mb-3">
<label for="container-image" class="form-label">Image</label>
<input type="text" class="form-control" id="container-image" required>
</div>
<div class="mb-3">
<label for="container-netmode" class="form-label">Net Mode</label>
<input type="text" class="form-control" id="container-netmode" required>
</div>
<div class="mb-3">
<label for="container-cpu" class="form-label">CPU Count</label>
<input type="number" class="form-control" id="container-cpu" required>
</div>
<div class="mb-3">
<label for="container-memory" class="form-label">Memory (MB)</label>
<input type="number" class="form-control" id="container-memory" required>
</div>
<div class="mb-3">
<label for="container-config" class="form-label">Container Configuration (JSON)</label>
<textarea class="form-control" id="container-config" rows="10" required></textarea>
</div>
<button type="submit" class="btn btn-primary">Deploy Duplicate</button>
</form>
</div>
</div>
</div>
</div>
<!-- Terminal Modal -->
<div id="terminal-modal">
<div class="header">
<span id="terminal-title"></span>
<div>
<button id="kill-terminal-btn" class="btn btn-sm btn-danger">
<i class="fas fa-times-circle"></i>
</button>
</div>
</div>
<div id="terminal-container"></div>
<div id="terminal-resize-handle"></div>
</div>
<!-- Delete Confirmation Modal -->
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content bg-dark text-white">
<div class="modal-header">
<h5 class="modal-title" id="deleteModalLabel">Confirm Deletion</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Are you sure you want to delete this container?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" id="confirm-delete-btn" class="btn btn-danger">Delete</button>
</div>
</div>
</div>
</div>
<!-- Status Indicator Overlay -->
<div id="status-indicator"
class="position-fixed top-0 start-0 w-100 h-100 d-flex justify-content-center align-items-center bg-dark bg-opacity-75"
style="display: none; z-index: 1050;">
<div class="text-center">
<div class="spinner-border text-light" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-3 text-light">Processing...</p>
</div>
</div>
<!-- Docker Terminal Modal -->
<div class="modal fade" id="dockerTerminalModal" tabindex="-1" aria-labelledby="docker-terminal-title" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content bg-dark text-white">
<div class="modal-header">
<h5 id="docker-terminal-title" class="modal-title">Docker CLI Terminal</h5>
<button id="docker-kill-terminal-btn" type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="docker-terminal-container"></div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="logsModal" tabindex="-1" aria-labelledby="logsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content bg-dark text-white">
<div class="modal-header">
<h5 class="modal-title" id="logsModalLabel">Container Logs</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="logs-container" style="max-height: 70vh; overflow-y: auto; font-family: monospace; background: black; padding: 10px;"></div>
</div>
</div>
</div>
</div>
<!-- Search Input -->
<!-- Deploy Modal -->
<div class="modal fade" id="templateDeployModal" tabindex="-1" aria-labelledby="deployModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content bg-dark text-white">
<div class="modal-header">
<h5 class="modal-title" id="deploy-title"></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<input type="text" id="template-search-input" class="form-control my-3" placeholder="Search templates...">
<ul id="template-list" class="list-group"></ul>
</div>
</div>
</div>
</div>
<!-- Template Deploy Modal -->
<div class="modal fade" id="templateDeployModal" tabindex="-1" aria-labelledby="templateDeployModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content bg-dark text-white">
<div class="modal-header">
<h5 class="modal-title" id="templateDeployModalLabel">Deploy Template</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<input type="text" id="template-search-input" class="form-control mb-3" placeholder="Search templates...">
<ul id="template-list" class="list-group"></ul>
</div>
</div>
</div>
</div>
<!-- Deploy Template Modal -->
<div class="modal fade" id="templateDeployModalUnique" tabindex="-1" aria-labelledby="templateDeployModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content bg-dark text-white">
<div class="modal-header">
<h5 class="modal-title" id="deploy-title">Deploy Template</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="deploy-form">
<div class="form-group mb-3">
<label for="deploy-container-name">Container Name</label>
<input type="text" id="deploy-container-name" class="form-control" placeholder="Enter container name">
</div>
<div class="mb-3">
<label for="deploy-image" class="form-label">Image</label>
<input type="text" id="deploy-image" class="form-control" required />
</div>
<div class="mb-3">
<label for="deploy-ports" class="form-label">Ports</label>
<input type="text" id="deploy-ports" class="form-control" placeholder="e.g., 80/tcp, 443/tcp" />
</div>
<div class="mb-3">
<label for="deploy-volumes" class="form-label">Volumes</label>
<input type="text" id="deploy-volumes" class="form-control" placeholder="e.g., /host/path:/container/path" />
</div>
<div class="mb-3">
<label for="deploy-env" class="form-label">Environment Variables</label>
<div id="deploy-env"></div>
</div>
<button type="submit" class="btn btn-primary">Deploy</button>
</form>
</div>
</div>
</div>
</div>
<!-- Alert Container -->
<div id="alert-container" class="position-fixed top-0 start-50 translate-middle-x mt-3"
style="z-index: 1051; max-width: 90%;"></div>
<!-- xterm.js -->
<script src="https://cdn.jsdelivr.net/npm/xterm/lib/xterm.js"></script>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<!-- Your App JS -->
<script type="module" src="app.js"></script>
</body>
</html>