1123 lines
44 KiB
HTML
1123 lines
44 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/[email protected]/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;
|
|
display: flex;
|
|
flex-direction: column-reverse;
|
|
gap: 10px;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.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 */
|
|
}
|
|
|
|
/* Accordion styling for deploy modal */
|
|
.accordion-item {
|
|
background-color: #2c2c2c;
|
|
border-color: #444;
|
|
}
|
|
|
|
.accordion-button {
|
|
background-color: #2c2c2c;
|
|
color: #fff;
|
|
}
|
|
|
|
.accordion-button:not(.collapsed) {
|
|
background-color: #3c3c3c;
|
|
color: #fff;
|
|
}
|
|
|
|
.accordion-button:focus {
|
|
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
|
|
}
|
|
|
|
.accordion-body {
|
|
background-color: #1a1a1a;
|
|
}
|
|
|
|
/* Form control styling in dark mode */
|
|
.form-control:focus,
|
|
.form-select:focus {
|
|
background-color: #2c2c2c;
|
|
border-color: #0d6efd;
|
|
color: #fff;
|
|
}
|
|
|
|
/* Array item styling */
|
|
.array-item {
|
|
display: flex;
|
|
gap: 8px;
|
|
margin-bottom: 8px;
|
|
align-items: center;
|
|
}
|
|
|
|
.array-item input {
|
|
flex: 1;
|
|
}
|
|
|
|
.array-item .btn-sm {
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.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"><</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 -->
|
|
|
|
<!-- 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-xl">
|
|
<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" style="max-height: 80vh; overflow-y: auto;">
|
|
<form id="deploy-form">
|
|
<!-- Basic Settings (Always Visible) -->
|
|
<div class="mb-4">
|
|
<h6 class="text-primary mb-3"><i class="fas fa-cog"></i> Basic Settings</h6>
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label for="deploy-container-name" class="form-label">Container Name <span class="text-danger">*</span></label>
|
|
<input type="text" id="deploy-container-name" class="form-control bg-dark text-white" placeholder="my-container" required>
|
|
<small class="text-muted">Alphanumeric, dashes, and underscores only</small>
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label for="deploy-image" class="form-label">Image <span class="text-danger">*</span></label>
|
|
<input type="text" id="deploy-image" class="form-control bg-dark text-white" placeholder="nginx:latest" required>
|
|
<small class="text-muted">Docker image name and tag</small>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-4 mb-3">
|
|
<label for="deploy-command" class="form-label">Command Override</label>
|
|
<input type="text" id="deploy-command" class="form-control bg-dark text-white" placeholder="Override default command">
|
|
<small class="text-muted">Override container CMD</small>
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<label for="deploy-entrypoint" class="form-label">Entrypoint Override</label>
|
|
<input type="text" id="deploy-entrypoint" class="form-control bg-dark text-white" placeholder="Override default entrypoint">
|
|
<small class="text-muted">Override container ENTRYPOINT</small>
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<label for="deploy-workdir" class="form-label">Working Directory</label>
|
|
<input type="text" id="deploy-workdir" class="form-control bg-dark text-white" placeholder="/app">
|
|
<small class="text-muted">Container working directory</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Accordion for Advanced Settings -->
|
|
<div class="accordion accordion-flush" id="deployAccordion">
|
|
<!-- Networking -->
|
|
<div class="accordion-item bg-dark border-secondary">
|
|
<h2 class="accordion-header">
|
|
<button class="accordion-button collapsed bg-dark text-white" type="button" data-bs-toggle="collapse" data-bs-target="#networkingCollapse">
|
|
<i class="fas fa-network-wired me-2"></i> Networking
|
|
</button>
|
|
</h2>
|
|
<div id="networkingCollapse" class="accordion-collapse collapse" data-bs-parent="#deployAccordion">
|
|
<div class="accordion-body">
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<label for="deploy-network-mode" class="form-label">Network Mode</label>
|
|
<select id="deploy-network-mode" class="form-select bg-dark text-white">
|
|
<option value="bridge">Bridge</option>
|
|
<option value="host">Host</option>
|
|
<option value="none">None</option>
|
|
<option value="container">Container</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6" id="deploy-custom-network-container" style="display: none;">
|
|
<label for="deploy-custom-network" class="form-label">Custom Network</label>
|
|
<input type="text" id="deploy-custom-network" class="form-control bg-dark text-white" placeholder="network-name">
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<label for="deploy-hostname" class="form-label">Hostname</label>
|
|
<input type="text" id="deploy-hostname" class="form-control bg-dark text-white" placeholder="container-hostname">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="deploy-domainname" class="form-label">Domain Name</label>
|
|
<input type="text" id="deploy-domainname" class="form-control bg-dark text-white" placeholder="example.com">
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Port Mappings</label>
|
|
<div id="deploy-ports-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addPortMapping()">
|
|
<i class="fas fa-plus"></i> Add Port
|
|
</button>
|
|
<small class="text-muted d-block mt-1">Format: host:container/protocol (e.g., 8080:80/tcp)</small>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">DNS Servers</label>
|
|
<div id="deploy-dns-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addDnsServer()">
|
|
<i class="fas fa-plus"></i> Add DNS Server
|
|
</button>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Extra Hosts</label>
|
|
<div id="deploy-extra-hosts-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addExtraHost()">
|
|
<i class="fas fa-plus"></i> Add Host
|
|
</button>
|
|
<small class="text-muted d-block mt-1">Format: hostname:ip (e.g., example.com:127.0.0.1)</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Volumes & Storage -->
|
|
<div class="accordion-item bg-dark border-secondary">
|
|
<h2 class="accordion-header">
|
|
<button class="accordion-button collapsed bg-dark text-white" type="button" data-bs-toggle="collapse" data-bs-target="#volumesCollapse">
|
|
<i class="fas fa-hdd me-2"></i> Volumes & Storage
|
|
</button>
|
|
</h2>
|
|
<div id="volumesCollapse" class="accordion-collapse collapse" data-bs-parent="#deployAccordion">
|
|
<div class="accordion-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">Volume Mounts</label>
|
|
<div id="deploy-volumes-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addVolumeMount()">
|
|
<i class="fas fa-plus"></i> Add Volume
|
|
</button>
|
|
<small class="text-muted d-block mt-1">Format: /host/path:/container/path:mode (mode: ro/rw)</small>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Tmpfs Mounts</label>
|
|
<div id="deploy-tmpfs-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addTmpfsMount()">
|
|
<i class="fas fa-plus"></i> Add Tmpfs
|
|
</button>
|
|
<small class="text-muted d-block mt-1">Format: /path:size (e.g., /tmp:100m)</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Resources -->
|
|
<div class="accordion-item bg-dark border-secondary">
|
|
<h2 class="accordion-header">
|
|
<button class="accordion-button collapsed bg-dark text-white" type="button" data-bs-toggle="collapse" data-bs-target="#resourcesCollapse">
|
|
<i class="fas fa-server me-2"></i> Resources
|
|
</button>
|
|
</h2>
|
|
<div id="resourcesCollapse" class="accordion-collapse collapse" data-bs-parent="#deployAccordion">
|
|
<div class="accordion-body">
|
|
<div class="row mb-3">
|
|
<div class="col-md-4">
|
|
<label for="deploy-cpu-limit" class="form-label">CPU Limit (cores)</label>
|
|
<input type="number" id="deploy-cpu-limit" class="form-control bg-dark text-white" placeholder="2" step="0.1" min="0">
|
|
<small class="text-muted">Number of CPU cores</small>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="deploy-cpu-reservation" class="form-label">CPU Reservation</label>
|
|
<input type="number" id="deploy-cpu-reservation" class="form-control bg-dark text-white" placeholder="1" step="0.1" min="0">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="deploy-cpu-shares" class="form-label">CPU Shares</label>
|
|
<input type="number" id="deploy-cpu-shares" class="form-control bg-dark text-white" placeholder="1024" min="0">
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-md-4">
|
|
<label for="deploy-memory-limit" class="form-label">Memory Limit (MB)</label>
|
|
<input type="number" id="deploy-memory-limit" class="form-control bg-dark text-white" placeholder="512" min="0">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="deploy-memory-reservation" class="form-label">Memory Reservation (MB)</label>
|
|
<input type="number" id="deploy-memory-reservation" class="form-control bg-dark text-white" placeholder="256" min="0">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="deploy-memory-swap" class="form-label">Memory Swap (MB)</label>
|
|
<input type="number" id="deploy-memory-swap" class="form-control bg-dark text-white" placeholder="-1" min="-1">
|
|
<small class="text-muted">-1 for unlimited</small>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Device Mappings</label>
|
|
<div id="deploy-devices-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addDeviceMapping()">
|
|
<i class="fas fa-plus"></i> Add Device
|
|
</button>
|
|
<small class="text-muted d-block mt-1">Format: /host/device:/container/device:permissions</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Environment & Labels -->
|
|
<div class="accordion-item bg-dark border-secondary">
|
|
<h2 class="accordion-header">
|
|
<button class="accordion-button collapsed bg-dark text-white" type="button" data-bs-toggle="collapse" data-bs-target="#envLabelsCollapse">
|
|
<i class="fas fa-tags me-2"></i> Environment & Labels
|
|
</button>
|
|
</h2>
|
|
<div id="envLabelsCollapse" class="accordion-collapse collapse" data-bs-parent="#deployAccordion">
|
|
<div class="accordion-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">Environment Variables</label>
|
|
<div id="deploy-env"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addEnvVar()">
|
|
<i class="fas fa-plus"></i> Add Variable
|
|
</button>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Labels</label>
|
|
<div id="deploy-labels-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addLabel()">
|
|
<i class="fas fa-plus"></i> Add Label
|
|
</button>
|
|
<small class="text-muted d-block mt-1">Format: key=value</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Security -->
|
|
<div class="accordion-item bg-dark border-secondary">
|
|
<h2 class="accordion-header">
|
|
<button class="accordion-button collapsed bg-dark text-white" type="button" data-bs-toggle="collapse" data-bs-target="#securityCollapse">
|
|
<i class="fas fa-shield-alt me-2"></i> Security
|
|
</button>
|
|
</h2>
|
|
<div id="securityCollapse" class="accordion-collapse collapse" data-bs-parent="#deployAccordion">
|
|
<div class="accordion-body">
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<label for="deploy-user" class="form-label">User/UID</label>
|
|
<input type="text" id="deploy-user" class="form-control bg-dark text-white" placeholder="1000 or username">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="deploy-group" class="form-label">Group/GID</label>
|
|
<input type="text" id="deploy-group" class="form-control bg-dark text-white" placeholder="1000 or groupname">
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="deploy-privileged">
|
|
<label class="form-check-label" for="deploy-privileged">Privileged Mode</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="deploy-readonly-rootfs">
|
|
<label class="form-check-label" for="deploy-readonly-rootfs">Read-only Root Filesystem</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Capabilities</label>
|
|
<div id="deploy-capabilities-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addCapability()">
|
|
<i class="fas fa-plus"></i> Add Capability
|
|
</button>
|
|
<small class="text-muted d-block mt-1">e.g., NET_ADMIN, SYS_ADMIN</small>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Security Options</label>
|
|
<div id="deploy-security-opts-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addSecurityOpt()">
|
|
<i class="fas fa-plus"></i> Add Option
|
|
</button>
|
|
<small class="text-muted d-block mt-1">e.g., apparmor=profile, seccomp=unconfined</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Runtime & Behavior -->
|
|
<div class="accordion-item bg-dark border-secondary">
|
|
<h2 class="accordion-header">
|
|
<button class="accordion-button collapsed bg-dark text-white" type="button" data-bs-toggle="collapse" data-bs-target="#runtimeCollapse">
|
|
<i class="fas fa-play-circle me-2"></i> Runtime & Behavior
|
|
</button>
|
|
</h2>
|
|
<div id="runtimeCollapse" class="accordion-collapse collapse" data-bs-parent="#deployAccordion">
|
|
<div class="accordion-body">
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<label for="deploy-restart-policy" class="form-label">Restart Policy</label>
|
|
<select id="deploy-restart-policy" class="form-select bg-dark text-white">
|
|
<option value="no">No</option>
|
|
<option value="always">Always</option>
|
|
<option value="on-failure">On Failure</option>
|
|
<option value="unless-stopped">Unless Stopped</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="deploy-restart-max-retries" class="form-label">Max Retries (on-failure)</label>
|
|
<input type="number" id="deploy-restart-max-retries" class="form-control bg-dark text-white" placeholder="10" min="0">
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-md-4">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="deploy-auto-remove">
|
|
<label class="form-check-label" for="deploy-auto-remove">Auto Remove</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="deploy-tty">
|
|
<label class="form-check-label" for="deploy-tty">TTY</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="deploy-stdin-open">
|
|
<label class="form-check-label" for="deploy-stdin-open">Interactive (Stdin Open)</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="deploy-detach">
|
|
<label class="form-check-label" for="deploy-detach">Detach Mode</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="deploy-init">
|
|
<label class="form-check-label" for="deploy-init">Init Process</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Health & Logging -->
|
|
<div class="accordion-item bg-dark border-secondary">
|
|
<h2 class="accordion-header">
|
|
<button class="accordion-button collapsed bg-dark text-white" type="button" data-bs-toggle="collapse" data-bs-target="#healthCollapse">
|
|
<i class="fas fa-heartbeat me-2"></i> Health & Logging
|
|
</button>
|
|
</h2>
|
|
<div id="healthCollapse" class="accordion-collapse collapse" data-bs-parent="#deployAccordion">
|
|
<div class="accordion-body">
|
|
<div class="mb-3">
|
|
<label for="deploy-health-cmd" class="form-label">Health Check Command</label>
|
|
<input type="text" id="deploy-health-cmd" class="form-control bg-dark text-white" placeholder="CMD-SHELL curl -f http://localhost/ || exit 1">
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-md-3">
|
|
<label for="deploy-health-interval" class="form-label">Interval (s)</label>
|
|
<input type="number" id="deploy-health-interval" class="form-control bg-dark text-white" placeholder="30" min="1">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label for="deploy-health-timeout" class="form-label">Timeout (s)</label>
|
|
<input type="number" id="deploy-health-timeout" class="form-control bg-dark text-white" placeholder="10" min="1">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label for="deploy-health-retries" class="form-label">Retries</label>
|
|
<input type="number" id="deploy-health-retries" class="form-control bg-dark text-white" placeholder="3" min="0">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label for="deploy-health-start-period" class="form-label">Start Period (s)</label>
|
|
<input type="number" id="deploy-health-start-period" class="form-control bg-dark text-white" placeholder="0" min="0">
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<label for="deploy-log-driver" class="form-label">Logging Driver</label>
|
|
<select id="deploy-log-driver" class="form-select bg-dark text-white">
|
|
<option value="">Default</option>
|
|
<option value="json-file">JSON File</option>
|
|
<option value="syslog">Syslog</option>
|
|
<option value="journald">Journald</option>
|
|
<option value="gelf">GELF</option>
|
|
<option value="fluentd">Fluentd</option>
|
|
<option value="awslogs">AWS Logs</option>
|
|
<option value="none">None</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label">Logging Options</label>
|
|
<div id="deploy-log-opts-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addLogOpt()">
|
|
<i class="fas fa-plus"></i> Add Option
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Advanced -->
|
|
<div class="accordion-item bg-dark border-secondary">
|
|
<h2 class="accordion-header">
|
|
<button class="accordion-button collapsed bg-dark text-white" type="button" data-bs-toggle="collapse" data-bs-target="#advancedCollapse">
|
|
<i class="fas fa-cogs me-2"></i> Advanced
|
|
</button>
|
|
</h2>
|
|
<div id="advancedCollapse" class="accordion-collapse collapse" data-bs-parent="#deployAccordion">
|
|
<div class="accordion-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">Sysctls</label>
|
|
<div id="deploy-sysctls-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addSysctl()">
|
|
<i class="fas fa-plus"></i> Add Sysctl
|
|
</button>
|
|
<small class="text-muted d-block mt-1">Format: key=value (e.g., net.ipv4.ip_forward=1)</small>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Ulimits</label>
|
|
<div id="deploy-ulimits-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addUlimit()">
|
|
<i class="fas fa-plus"></i> Add Ulimit
|
|
</button>
|
|
<small class="text-muted d-block mt-1">Format: name=soft:hard (e.g., nofile=1024:2048)</small>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="deploy-oom-kill-disable">
|
|
<label class="form-check-label" for="deploy-oom-kill-disable">Disable OOM Killer</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="deploy-pids-limit" class="form-label">PIDs Limit</label>
|
|
<input type="number" id="deploy-pids-limit" class="form-control bg-dark text-white" placeholder="-1" min="-1">
|
|
<small class="text-muted">-1 for unlimited</small>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="deploy-shm-size" class="form-label">Shared Memory Size (MB)</label>
|
|
<input type="number" id="deploy-shm-size" class="form-control bg-dark text-white" placeholder="64" min="0">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Preview Section -->
|
|
<div class="mt-4 mb-3">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="deploy-show-preview" onchange="togglePreview()">
|
|
<label class="form-check-label" for="deploy-show-preview">Show JSON Preview</label>
|
|
</div>
|
|
<div id="deploy-preview-container" style="display: none;" class="mt-2">
|
|
<pre id="deploy-preview" class="bg-dark text-white p-3 rounded" style="max-height: 300px; overflow-y: auto; font-size: 12px;"></pre>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-between mt-4">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-rocket"></i> Deploy Container
|
|
</button>
|
|
</div>
|
|
</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/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
|
|
|
|
<!-- Your App JS -->
|
|
<script type="module" src="app.js"></script>
|
|
</body>
|
|
|
|
</html> |