CI / test (push) Successful in 9m55s
Keep peer action buttons from overflowing with stable truncation and short labels, move reset peers into Settings with an infinitely scrollable peer list, and harden waitForPeerResponse so success replies without a message (listVolumes) no longer throw.
3463 lines
180 KiB
HTML
3463 lines
180 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>peardock</title>
|
|
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
<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.5.2/css/all.min.css" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/css/xterm.css">
|
|
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script>
|
|
<link rel="stylesheet" href="ui/styles.css">
|
|
<link rel="stylesheet" href="ui/modern.css">
|
|
<link rel="stylesheet" href="ui/ops.css">
|
|
|
|
|
|
</head>
|
|
|
|
<body>
|
|
<div id="offline-banner" role="status" aria-live="polite">
|
|
<i class="fas fa-plug-circle-xmark"></i>
|
|
<span id="offline-banner-text">Connection lost — reconnecting…</span>
|
|
<button type="button" class="btn btn-sm btn-outline-light" id="offline-banner-dismiss">Dismiss</button>
|
|
</div>
|
|
<div id="titlebar">
|
|
<div class="titlebar-left">
|
|
<pear-ctrl></pear-ctrl>
|
|
<div class="app-brand">
|
|
<div class="app-brand-mark">🍐</div>
|
|
<div class="app-brand-text">peardock <span>P2P Docker</span></div>
|
|
</div>
|
|
</div>
|
|
<div class="titlebar-right">
|
|
<div id="active-peer-chip" title="Active peer">
|
|
<span class="dot" id="active-peer-dot"></span>
|
|
<span id="active-peer-label">No peer</span>
|
|
</div>
|
|
<div id="connection-health-badge" class="connection-health-badge" title="Connection health">
|
|
<span class="health-dot health-dot--unknown" id="health-dot"></span>
|
|
<span id="health-latency" class="health-latency">—</span>
|
|
<span id="health-docker" class="health-docker">Docker —</span>
|
|
<span id="health-role" class="health-role"></span>
|
|
</div>
|
|
<!-- Notification Tray - Global across all views -->
|
|
<div id="notification-tray" class="notification-tray">
|
|
<button id="notification-tray-toggle" class="notification-tray-toggle" aria-label="Notifications">
|
|
<i class="fas fa-bell"></i>
|
|
<span id="notification-badge" class="notification-badge">0</span>
|
|
</button>
|
|
<div id="notification-panel" class="notification-panel hidden">
|
|
<div class="notification-panel-header">
|
|
<h5 class="notification-panel-title">Notifications</h5>
|
|
<div class="notification-panel-actions">
|
|
<button id="mark-all-read-btn" class="notification-action-btn" title="Mark all as read">
|
|
<i class="fas fa-check-double"></i>
|
|
</button>
|
|
<button id="clear-all-btn" class="notification-action-btn" title="Clear all">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
<button id="close-panel-btn" class="notification-action-btn" title="Close">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="notification-filters">
|
|
<button class="notification-filter-btn active" data-filter="all">All</button>
|
|
<button class="notification-filter-btn" data-filter="success">Success</button>
|
|
<button class="notification-filter-btn" data-filter="danger">Error</button>
|
|
<button class="notification-filter-btn" data-filter="warning">Warning</button>
|
|
<button class="notification-filter-btn" data-filter="info">Info</button>
|
|
</div>
|
|
<div id="notification-list" class="notification-list">
|
|
<div class="notification-empty">
|
|
<i class="fas fa-bell-slash"></i>
|
|
<p>No notifications</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="sidebar">
|
|
<button id="collapse-sidebar-btn" title="Collapse sidebar" aria-label="Collapse sidebar">
|
|
<i class="fas fa-chevron-left"></i>
|
|
</button>
|
|
<div class="content">
|
|
<div class="sidebar-nav-scroll">
|
|
<div class="sidebar-section-label">Navigate <kbd class="text-muted" style="font-size:0.65rem">Ctrl+K</kbd></div>
|
|
<nav id="main-nav">
|
|
<ul class="nav-menu">
|
|
<li class="nav-group-label">Overview</li>
|
|
<li class="nav-item">
|
|
<a href="#" class="nav-link active" data-view="dashboard" title="Dashboard">
|
|
<i class="fas fa-gauge-high"></i>
|
|
<span class="nav-label">Dashboard</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="#" class="nav-link" data-view="fleet" title="Fleet">
|
|
<i class="fas fa-server"></i>
|
|
<span class="nav-label">Fleet</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="#" class="nav-link" data-view="events" title="Events">
|
|
<i class="fas fa-bolt"></i>
|
|
<span class="nav-label">Events</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-group-label">Workloads</li>
|
|
<li class="nav-item">
|
|
<a href="#" class="nav-link" data-view="deploy" title="Deploy">
|
|
<i class="fas fa-rocket"></i>
|
|
<span class="nav-label">Deploy</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="#" class="nav-link" data-view="containers" title="Containers">
|
|
<i class="fas fa-cube"></i>
|
|
<span class="nav-label">Containers</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="#" class="nav-link" data-view="stacks" title="Stacks">
|
|
<i class="fas fa-boxes-stacked"></i>
|
|
<span class="nav-label">Stacks</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-group-label">Build & storage</li>
|
|
<li class="nav-item">
|
|
<a href="#" class="nav-link" data-view="images" title="Images">
|
|
<i class="fas fa-layer-group"></i>
|
|
<span class="nav-label">Images</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="#" class="nav-link" data-view="volumes" title="Volumes">
|
|
<i class="fas fa-hard-drive"></i>
|
|
<span class="nav-label">Volumes</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="#" class="nav-link" data-view="networks" title="Networks">
|
|
<i class="fas fa-diagram-project"></i>
|
|
<span class="nav-label">Networks</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-group-label">Host & access</li>
|
|
<li class="nav-item">
|
|
<a href="#" class="nav-link" data-view="host" title="Host">
|
|
<i class="fas fa-microchip"></i>
|
|
<span class="nav-label">Host</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="#" class="nav-link" data-view="access" title="Access">
|
|
<i class="fas fa-user-shield"></i>
|
|
<span class="nav-label">Access</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="#" class="nav-link" data-view="settings" title="Settings">
|
|
<i class="fas fa-gear"></i>
|
|
<span class="nav-label">Settings</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
|
|
<hr class="sidebar-divider">
|
|
|
|
<div class="connections-panel">
|
|
<div class="connections-panel-header">
|
|
<div class="sidebar-section-label mb-0">Peers</div>
|
|
<button type="button" id="open-add-connection-btn" class="btn btn-sm btn-primary open-add-connection-btn" title="Add peer" aria-label="Add peer">
|
|
<i class="fas fa-plus"></i>
|
|
<span class="open-add-connection-label">Add</span>
|
|
</button>
|
|
</div>
|
|
<ul id="connection-list" class="list-group"></ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div id="content">
|
|
<div id="welcome-page">
|
|
<h1>Welcome to peardock</h1>
|
|
<p class="welcome-lead">Peer-to-peer Docker control plane over HyperDHT and protomux-rpc. No central server. No cloud account.</p>
|
|
<div class="welcome-steps">
|
|
<div class="welcome-step">
|
|
<div class="welcome-step-num">1</div>
|
|
<div>
|
|
<strong>Start the server</strong>
|
|
<span>On a host with Docker: <span class="welcome-mono">npm run server</span></span>
|
|
</div>
|
|
</div>
|
|
<div class="welcome-step">
|
|
<div class="welcome-step-num">2</div>
|
|
<div>
|
|
<strong>Copy the public key</strong>
|
|
<span>Printed at startup — 64 hex characters. Never share the seed.</span>
|
|
</div>
|
|
</div>
|
|
<div class="welcome-step">
|
|
<div class="welcome-step-num">3</div>
|
|
<div>
|
|
<strong>Connect from here</strong>
|
|
<span>Use <em>Add peer</em> to paste the public key (and optional invite token), then manage containers, images, stacks, and terminals.</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button type="button" id="welcome-add-peer-btn" class="btn btn-primary btn-lg mt-4">
|
|
<i class="fas fa-plug me-2"></i>Add peer
|
|
</button>
|
|
</div>
|
|
<!-- Dashboard View -->
|
|
<div id="dashboard-view" class="view hidden">
|
|
<div class="container-fluid">
|
|
<div class="page-header">
|
|
<div>
|
|
<h2><i class="fas fa-gauge-high"></i>Dashboard</h2>
|
|
<p class="page-subtitle">Host overview and quick actions</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- System Overview Cards -->
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-md-3">
|
|
<div class="stat-card">
|
|
<div class="stat-card-icon" style="background: rgba(16, 185, 129, 0.2); color: var(--accent-success);">
|
|
<i class="fas fa-box"></i>
|
|
</div>
|
|
<div class="stat-card-content">
|
|
<div class="stat-card-value" id="stat-running-containers">0</div>
|
|
<div class="stat-card-label">Running Containers</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="stat-card">
|
|
<div class="stat-card-icon" style="background: rgba(239, 68, 68, 0.2); color: var(--accent-danger);">
|
|
<i class="fas fa-stop-circle"></i>
|
|
</div>
|
|
<div class="stat-card-content">
|
|
<div class="stat-card-value" id="stat-stopped-containers">0</div>
|
|
<div class="stat-card-label">Stopped Containers</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="stat-card">
|
|
<div class="stat-card-icon" style="background: rgba(59, 130, 246, 0.2); color: var(--accent-info);">
|
|
<i class="fas fa-images"></i>
|
|
</div>
|
|
<div class="stat-card-content">
|
|
<div class="stat-card-value" id="stat-total-images">0</div>
|
|
<div class="stat-card-label">Total Images</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="stat-card">
|
|
<div class="stat-card-icon" style="background: rgba(20, 184, 166, 0.2); color: var(--accent-secondary);">
|
|
<i class="fas fa-network-wired"></i>
|
|
</div>
|
|
<div class="stat-card-content">
|
|
<div class="stat-card-value" id="stat-total-networks">0</div>
|
|
<div class="stat-card-label">Networks</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- System Information -->
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-md-6">
|
|
<div class="info-card">
|
|
<h5 class="info-card-title"><i class="fas fa-info-circle me-2"></i>Docker Information</h5>
|
|
<div id="docker-info-content" class="info-card-body">
|
|
<div class="skeleton skeleton-text"></div>
|
|
<div class="skeleton skeleton-text mt-2"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="info-card">
|
|
<h5 class="info-card-title"><i class="fas fa-server me-2"></i>System Resources</h5>
|
|
<div id="system-resources-content" class="info-card-body">
|
|
<div class="skeleton skeleton-text"></div>
|
|
<div class="skeleton skeleton-text mt-2"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Disk usage + live events -->
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-md-6">
|
|
<div class="info-card">
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<h5 class="info-card-title mb-0"><i class="fas fa-hdd me-2"></i>Disk Usage</h5>
|
|
<button class="btn btn-sm btn-outline-secondary" type="button" onclick="window.refreshSystemDf && window.refreshSystemDf()" title="Refresh system df">
|
|
<i class="fas fa-sync-alt"></i>
|
|
</button>
|
|
</div>
|
|
<div id="system-df-content" class="info-card-body">
|
|
<div class="text-muted small">Connect to load Docker disk usage…</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="info-card">
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<h5 class="info-card-title mb-0"><i class="fas fa-stream me-2"></i>Live Events</h5>
|
|
<button class="btn btn-sm btn-outline-secondary" type="button" onclick="window.clearEventTimeline && window.clearEventTimeline()" title="Clear">
|
|
<i class="fas fa-eraser"></i>
|
|
</button>
|
|
</div>
|
|
<div id="docker-events-timeline" class="info-card-body events-timeline">
|
|
<div class="text-muted small">Docker events will appear here…</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Actions -->
|
|
<div class="row g-3">
|
|
<div class="col-12">
|
|
<div class="info-card">
|
|
<h5 class="info-card-title"><i class="fas fa-bolt me-2"></i>Quick Actions</h5>
|
|
<div class="info-card-body">
|
|
<div class="d-flex gap-2 flex-wrap">
|
|
<button class="btn btn-primary" onclick="window.navigateToView('containers')">
|
|
<i class="fas fa-box me-2"></i>View Containers
|
|
</button>
|
|
<button class="btn btn-outline-primary" onclick="window.navigateToView('images')">
|
|
<i class="fas fa-images me-2"></i>Manage Images
|
|
</button>
|
|
<button class="btn btn-outline-primary" onclick="window.navigateToView('networks')">
|
|
<i class="fas fa-network-wired me-2"></i>Manage Networks
|
|
</button>
|
|
<button class="btn btn-outline-primary" onclick="window.navigateToView('volumes')">
|
|
<i class="fas fa-database me-2"></i>Manage Volumes
|
|
</button>
|
|
<button class="btn btn-outline-warning" type="button" onclick="window.pruneResource && window.pruneResource('containers')" title="Prune stopped containers">
|
|
<i class="fas fa-broom me-2"></i>Prune Containers
|
|
</button>
|
|
<button class="btn btn-outline-warning" type="button" onclick="window.pruneResource && window.pruneResource('images')" title="Prune unused images">
|
|
<i class="fas fa-broom me-2"></i>Prune Images
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Deploy View -->
|
|
<div id="deploy-view" class="view hidden">
|
|
<div class="container-fluid">
|
|
<div class="page-header">
|
|
<div>
|
|
<h2><i class="fas fa-rocket"></i>Deploy</h2>
|
|
<p class="page-subtitle">Templates, compose stacks, and custom images</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Template Selection Section -->
|
|
<div class="info-card mb-4">
|
|
<h5 class="info-card-title"><i class="fas fa-cubes me-2"></i>Select Template</h5>
|
|
<div class="info-card-body" style="display: flex; flex-direction: column; height: calc(100vh - 250px);">
|
|
<div class="d-flex align-items-center gap-2 mb-2">
|
|
<input type="search" id="deploy-template-search-input" class="form-control bg-dark text-white" placeholder="Search templates (debounced)…" autocomplete="off" spellcheck="false">
|
|
</div>
|
|
<div class="small text-muted mb-2" id="deploy-template-count">—</div>
|
|
<div id="deploy-template-list-container" style="flex: 1; overflow-y: auto; overflow-x: hidden; min-height: 0;">
|
|
<div class="text-center text-muted py-4">
|
|
<i class="fas fa-spinner fa-spin me-2"></i>Loading templates...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Deploy Form Section -->
|
|
<div class="info-card" id="deploy-form-section" style="display: none;">
|
|
<h5 class="info-card-title"><i class="fas fa-cog me-2"></i>Container Configuration</h5>
|
|
<div class="info-card-body">
|
|
<form id="deploy-view-form" onsubmit="return false;">
|
|
<!-- 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 - Reuse same structure as modal -->
|
|
<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-6">
|
|
<label for="deploy-cpu-limit" class="form-label">CPU Limit: <strong id="deploy-cpu-limit-val">Unlimited</strong></label>
|
|
<input type="range" class="form-range" id="deploy-cpu-limit" min="0" max="16" step="0.1" value="0">
|
|
<small class="text-muted">0 = unlimited · cores</small>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="deploy-cpu-reservation" class="form-label">CPU Reservation: <strong id="deploy-cpu-reservation-val">None</strong></label>
|
|
<input type="range" class="form-range" id="deploy-cpu-reservation" min="0" max="16" step="0.1" value="0">
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<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 class="col-md-4">
|
|
<label for="deploy-memory-limit" class="form-label">Memory Limit: <strong id="deploy-memory-limit-val">Unlimited</strong></label>
|
|
<input type="range" class="form-range" id="deploy-memory-limit" min="0" max="16384" step="64" value="0">
|
|
<small class="text-muted">0 = unlimited · MB</small>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="deploy-memory-reservation" class="form-label">Memory Reservation: <strong id="deploy-memory-reservation-val">None</strong></label>
|
|
<input type="range" class="form-range" id="deploy-memory-reservation" min="0" max="16384" step="64" value="0">
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<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 form-switch">
|
|
<input class="form-check-input" type="checkbox" role="switch" 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 form-switch">
|
|
<input class="form-check-input" type="checkbox" role="switch" 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 form-switch">
|
|
<input class="form-check-input" type="checkbox" role="switch" 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 form-switch">
|
|
<input class="form-check-input" type="checkbox" role="switch" id="deploy-tty">
|
|
<label class="form-check-label" for="deploy-tty">TTY</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-check form-switch">
|
|
<input class="form-check-input" type="checkbox" role="switch" 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 form-switch">
|
|
<input class="form-check-input" type="checkbox" role="switch" id="deploy-detach" checked>
|
|
<label class="form-check-label" for="deploy-detach">Detach Mode</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-check form-switch">
|
|
<input class="form-check-input" type="checkbox" role="switch" 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 form-switch">
|
|
<input class="form-check-input" type="checkbox" role="switch" 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" onclick="resetDeployView()">
|
|
<i class="fas fa-undo me-2"></i>Reset
|
|
</button>
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-rocket me-2"></i>Deploy Container
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Containers View -->
|
|
<div id="containers-view" class="view hidden">
|
|
<div class="container-fluid">
|
|
<div class="page-header">
|
|
<div>
|
|
<h2><i class="fas fa-cube"></i>Containers</h2>
|
|
<p class="page-subtitle">Lifecycle, logs, stats, and terminals</p>
|
|
</div>
|
|
</div>
|
|
<!-- Container Filter and Search -->
|
|
<div class="view-toolbar mb-3">
|
|
<div class="row g-2 w-100 m-0">
|
|
<div class="col-md-4 px-1">
|
|
<input type="text" id="container-search" class="form-control" placeholder="Search containers by name or image…">
|
|
</div>
|
|
<div class="col-md-3 px-1">
|
|
<select id="container-status-filter" class="form-select">
|
|
<option value="all">All Status</option>
|
|
<option value="running">Running</option>
|
|
<option value="exited">Stopped</option>
|
|
<option value="created">Created</option>
|
|
<option value="restarting">Restarting</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3 px-1">
|
|
<select id="container-sort" class="form-select">
|
|
<option value="name-asc">Sort by Name (A-Z)</option>
|
|
<option value="name-desc">Sort by Name (Z-A)</option>
|
|
<option value="cpu-desc">Sort by CPU (High-Low)</option>
|
|
<option value="cpu-asc">Sort by CPU (Low-High)</option>
|
|
<option value="memory-desc">Sort by Memory (High-Low)</option>
|
|
<option value="memory-asc">Sort by Memory (Low-High)</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2 px-1">
|
|
<div class="d-flex gap-2 align-items-center">
|
|
<button id="clear-filters" class="btn btn-outline-secondary flex-grow-1">Clear</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Bulk Actions Toolbar -->
|
|
<div id="bulk-actions-toolbar" class="view-toolbar mb-3" style="display: none;">
|
|
<div class="d-flex align-items-center justify-content-between">
|
|
<div>
|
|
<span id="selected-count" class="text-primary fw-bold">0</span>
|
|
<span class="text-secondary"> container(s) selected</span>
|
|
</div>
|
|
<div class="btn-group">
|
|
<button class="btn btn-sm btn-outline-success" onclick="bulkStartContainers()" title="Start Selected">
|
|
<i class="fas fa-play me-1"></i>Start
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-warning" onclick="bulkStopContainers()" title="Stop Selected">
|
|
<i class="fas fa-stop me-1"></i>Stop
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-danger" onclick="bulkKillContainers()" title="Kill Selected">
|
|
<i class="fas fa-skull me-1"></i>Kill
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-danger" onclick="bulkRemoveContainers()" title="Remove Selected">
|
|
<i class="fas fa-trash me-1"></i>Remove
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-secondary" onclick="window.pruneResource && window.pruneResource('containers')" title="Prune stopped">
|
|
<i class="fas fa-broom me-1"></i>Prune
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-secondary" onclick="clearContainerSelection()" title="Clear Selection">
|
|
<i class="fas fa-times me-1"></i>Clear
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table class="table table-dark table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th style="width: 40px;">
|
|
<input type="checkbox" id="select-all-containers" onchange="toggleSelectAllContainers(this)">
|
|
</th>
|
|
<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>
|
|
|
|
<!-- Images View -->
|
|
<div id="images-view" class="view hidden">
|
|
<div class="container-fluid">
|
|
<div class="page-header">
|
|
<div>
|
|
<h2><i class="fas fa-layer-group"></i>Images</h2>
|
|
<p class="page-subtitle">Local image inventory and actions</p>
|
|
</div>
|
|
<div class="btn-group">
|
|
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#pullImageModal">
|
|
<i class="fas fa-download me-2"></i>Pull Image
|
|
</button>
|
|
<button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#buildImageModal">
|
|
<i class="fas fa-hammer me-2"></i>Build Image
|
|
</button>
|
|
<button class="btn btn-outline-warning" type="button" onclick="window.pruneResource && window.pruneResource('images')">
|
|
<i class="fas fa-broom me-2"></i>Prune
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Image Filters -->
|
|
<div class="view-toolbar mb-3">
|
|
<div class="btn-group" role="group" aria-label="Image filters">
|
|
<button type="button" class="btn btn-outline-primary image-filter-btn active" data-filter="all" onclick="filterImages('all')">
|
|
<i class="fas fa-list me-2"></i>All
|
|
<span id="filter-count-all" class="badge bg-secondary ms-2">0</span>
|
|
</button>
|
|
<button type="button" class="btn btn-outline-primary image-filter-btn" data-filter="used" onclick="filterImages('used')">
|
|
<i class="fas fa-check-circle me-2"></i>Used
|
|
<span id="filter-count-used" class="badge bg-success ms-2">0</span>
|
|
</button>
|
|
<button type="button" class="btn btn-outline-primary image-filter-btn" data-filter="unused" onclick="filterImages('unused')">
|
|
<i class="fas fa-times-circle me-2"></i>Unused
|
|
<span id="filter-count-unused" class="badge bg-warning ms-2">0</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bulk Actions Toolbar for Images -->
|
|
<div id="bulk-actions-images-toolbar" class="view-toolbar mb-3" style="display: none;">
|
|
<div class="d-flex align-items-center justify-content-between">
|
|
<div>
|
|
<span id="selected-images-count" class="text-primary fw-bold">0</span>
|
|
<span class="text-secondary"> image(s) selected</span>
|
|
</div>
|
|
<div class="btn-group">
|
|
<button class="btn btn-sm btn-outline-danger" onclick="bulkRemoveImages()" title="Remove Selected">
|
|
<i class="fas fa-trash me-1"></i>Remove
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-secondary" onclick="clearImageSelection()" title="Clear Selection">
|
|
<i class="fas fa-times me-1"></i>Clear
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table class="table table-dark table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th style="width: 40px;">
|
|
<input type="checkbox" id="select-all-images" onchange="toggleSelectAllImages(this)">
|
|
</th>
|
|
<th>Repository</th>
|
|
<th>Tag</th>
|
|
<th>Image ID</th>
|
|
<th>Size</th>
|
|
<th>Created</th>
|
|
<th>Usage</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="images-list"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Networks View -->
|
|
<div id="networks-view" class="view hidden">
|
|
<div class="container-fluid">
|
|
<div class="page-header">
|
|
<div>
|
|
<h2><i class="fas fa-diagram-project"></i>Networks</h2>
|
|
<p class="page-subtitle">Bridge, overlay, and container networks</p>
|
|
</div>
|
|
<div class="btn-group">
|
|
<button class="btn btn-primary" type="button" id="networks-create-btn" data-action="create-network">
|
|
<i class="fas fa-plus me-2"></i>Create Network
|
|
</button>
|
|
<button class="btn btn-outline-warning" type="button" onclick="window.pruneResource && window.pruneResource('networks')">
|
|
<i class="fas fa-broom me-2"></i>Prune
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table class="table table-dark table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Driver</th>
|
|
<th>Scope</th>
|
|
<th>Subnet</th>
|
|
<th>Gateway</th>
|
|
<th>Containers</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="networks-list"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Container Details View -->
|
|
<div id="container-details-view" class="view hidden">
|
|
<div class="container-fluid">
|
|
<div class="d-flex align-items-center mb-4">
|
|
<button class="btn btn-outline-secondary me-3" onclick="navigateToView('containers')">
|
|
<i class="fas fa-arrow-left me-2"></i>Back to Containers
|
|
</button>
|
|
<h2 class="mb-0" id="container-details-title">
|
|
<i class="fas fa-box me-2"></i>Container Details
|
|
</h2>
|
|
</div>
|
|
|
|
<!-- Container Details Tabs -->
|
|
<ul class="nav nav-tabs mb-4" id="container-details-tabs" role="tablist">
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link active" id="overview-tab" data-bs-toggle="tab" data-bs-target="#overview-pane" type="button" role="tab">
|
|
<i class="fas fa-info-circle me-2"></i>Overview
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="logs-tab" data-bs-toggle="tab" data-bs-target="#logs-pane" type="button" role="tab">
|
|
<i class="fas fa-list-alt me-2"></i>Logs
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="terminal-tab" data-bs-toggle="tab" data-bs-target="#terminal-pane" type="button" role="tab">
|
|
<i class="fas fa-terminal me-2"></i>Terminal
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="stats-tab" data-bs-toggle="tab" data-bs-target="#stats-pane" type="button" role="tab">
|
|
<i class="fas fa-chart-line me-2"></i>Stats
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="processes-tab" data-bs-toggle="tab" data-bs-target="#processes-pane" type="button" role="tab">
|
|
<i class="fas fa-microchip me-2"></i>Processes
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="config-tab" data-bs-toggle="tab" data-bs-target="#config-pane" type="button" role="tab">
|
|
<i class="fas fa-cog me-2"></i>Configuration
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="networking-tab" data-bs-toggle="tab" data-bs-target="#networking-pane" type="button" role="tab">
|
|
<i class="fas fa-network-wired me-2"></i>Networking
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- Tab Content -->
|
|
<div class="tab-content" id="container-details-tab-content">
|
|
<!-- Overview Pane -->
|
|
<div class="tab-pane fade show active" id="overview-pane" role="tabpanel">
|
|
<div id="container-overview-content" class="info-card">
|
|
<div class="skeleton skeleton-text"></div>
|
|
<div class="skeleton skeleton-text mt-2"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Logs Pane -->
|
|
<div class="tab-pane fade" id="logs-pane" role="tabpanel">
|
|
<div class="detail-section-card" style="padding: 0;">
|
|
<!-- Logs Toolbar -->
|
|
<div class="logs-toolbar">
|
|
<div class="logs-toolbar-left">
|
|
<div class="logs-search-container">
|
|
<i class="fas fa-search logs-search-icon"></i>
|
|
<input type="text" id="logs-search-input" class="logs-search-input" placeholder="Search logs...">
|
|
<button class="logs-clear-search" id="logs-clear-search" title="Clear search" style="display: none;">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
<div class="logs-filter-buttons">
|
|
<button class="logs-filter-btn active" data-filter="all" title="All logs">
|
|
<i class="fas fa-list"></i> All
|
|
</button>
|
|
<button class="logs-filter-btn" data-filter="error" title="Errors only">
|
|
<i class="fas fa-exclamation-circle"></i> Error
|
|
</button>
|
|
<button class="logs-filter-btn" data-filter="warn" title="Warnings only">
|
|
<i class="fas fa-exclamation-triangle"></i> Warn
|
|
</button>
|
|
<button class="logs-filter-btn" data-filter="info" title="Info only">
|
|
<i class="fas fa-info-circle"></i> Info
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="logs-toolbar-right">
|
|
<div class="logs-controls">
|
|
<label class="logs-toggle-label" title="Auto-scroll">
|
|
<input type="checkbox" id="logs-auto-scroll" checked>
|
|
<span><i class="fas fa-arrow-down"></i> Auto-scroll</span>
|
|
</label>
|
|
<button class="logs-control-btn" id="logs-pause-btn" title="Pause/Resume">
|
|
<i class="fas fa-pause"></i> Pause
|
|
</button>
|
|
<button class="logs-control-btn" id="logs-clear-btn" title="Clear logs">
|
|
<i class="fas fa-trash"></i> Clear
|
|
</button>
|
|
<button class="logs-control-btn" id="logs-copy-btn" title="Copy all logs">
|
|
<i class="fas fa-copy"></i> Copy
|
|
</button>
|
|
<button class="logs-control-btn" id="logs-download-btn" title="Download logs">
|
|
<i class="fas fa-download"></i> Download
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Logs Container -->
|
|
<div id="container-logs-content" class="logs-container">
|
|
<div class="text-muted text-center logs-loading">Loading logs...</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Terminal Pane -->
|
|
<div class="tab-pane fade" id="terminal-pane" role="tabpanel">
|
|
<div class="detail-section-card" style="padding: 0;">
|
|
<!-- Terminal Toolbar -->
|
|
<div class="terminal-toolbar">
|
|
<div class="terminal-toolbar-left">
|
|
<div class="terminal-controls-group">
|
|
<button class="terminal-control-btn" id="terminal-font-decrease" title="Decrease font size">
|
|
<i class="fas fa-minus"></i>
|
|
</button>
|
|
<span class="terminal-font-size" id="terminal-font-size-display">14</span>
|
|
<button class="terminal-control-btn" id="terminal-font-increase" title="Increase font size">
|
|
<i class="fas fa-plus"></i>
|
|
</button>
|
|
<button class="terminal-control-btn" id="terminal-font-reset" title="Reset font size">
|
|
<i class="fas fa-undo"></i>
|
|
</button>
|
|
</div>
|
|
<div class="terminal-controls-divider"></div>
|
|
<div class="terminal-controls-group">
|
|
<button class="terminal-control-btn" id="terminal-copy-btn" title="Copy selection">
|
|
<i class="fas fa-copy"></i> Copy
|
|
</button>
|
|
<button class="terminal-control-btn" id="terminal-clear-btn" title="Clear terminal">
|
|
<i class="fas fa-trash"></i> Clear
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="terminal-toolbar-right">
|
|
<div class="terminal-controls-group">
|
|
<select class="terminal-theme-select" id="terminal-theme-select" title="Select theme">
|
|
<option value="dark">Dark</option>
|
|
<option value="light">Light</option>
|
|
<option value="solarized-dark">Solarized Dark</option>
|
|
<option value="solarized-light">Solarized Light</option>
|
|
<option value="monokai">Monokai</option>
|
|
</select>
|
|
<button class="terminal-control-btn" id="terminal-settings-btn" title="Terminal settings">
|
|
<i class="fas fa-cog"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Terminal Container -->
|
|
<div id="container-terminal-content" class="terminal-container-wrapper">
|
|
<div id="container-terminal-xterm" class="terminal-xterm-container"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stats Pane -->
|
|
<div class="tab-pane fade" id="stats-pane" role="tabpanel">
|
|
<div id="container-stats-content" class="info-card">
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<div class="stat-card">
|
|
<div class="stat-card-icon" style="background: rgba(16, 185, 129, 0.2); color: var(--accent-success);">
|
|
<i class="fas fa-microchip"></i>
|
|
</div>
|
|
<div class="stat-card-content">
|
|
<div class="stat-card-value" id="detail-cpu">0%</div>
|
|
<div class="stat-card-label">CPU Usage</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="stat-card">
|
|
<div class="stat-card-icon" style="background: rgba(59, 130, 246, 0.2); color: var(--accent-info);">
|
|
<i class="fas fa-memory"></i>
|
|
</div>
|
|
<div class="stat-card-content">
|
|
<div class="stat-card-value" id="detail-memory">0 MB</div>
|
|
<div class="stat-card-label">Memory Usage</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="stats-charts-container" class="mt-4">
|
|
<p class="text-muted">Stats charts will be displayed here</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Processes Pane (docker top) -->
|
|
<div class="tab-pane fade" id="processes-pane" role="tabpanel">
|
|
<div class="info-card">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h5 class="mb-0"><i class="fas fa-microchip me-2"></i>Running Processes</h5>
|
|
<button class="btn btn-sm btn-outline-primary" type="button" id="refresh-container-top-btn" title="Refresh process list">
|
|
<i class="fas fa-sync-alt me-1"></i>Refresh
|
|
</button>
|
|
</div>
|
|
<div id="container-top-content" class="table-responsive">
|
|
<div class="text-muted small">Open this tab on a running container to list processes.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Configuration Pane -->
|
|
<div class="tab-pane fade" id="config-pane" role="tabpanel">
|
|
<div id="container-config-content" class="info-card">
|
|
<div class="skeleton skeleton-text"></div>
|
|
<div class="skeleton skeleton-text mt-2"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Networking Pane -->
|
|
<div class="tab-pane fade" id="networking-pane" role="tabpanel">
|
|
<div id="container-networking-content" class="info-card">
|
|
<div class="skeleton skeleton-text"></div>
|
|
<div class="skeleton skeleton-text mt-2"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Volumes View -->
|
|
<div id="stacks-view" class="view hidden">
|
|
<div class="container-fluid">
|
|
<div class="page-header">
|
|
<div>
|
|
<h2><i class="fas fa-boxes-stacked"></i>Stacks</h2>
|
|
<p class="page-subtitle">Compose stacks on the remote host</p>
|
|
</div>
|
|
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#deploy-stack-modal">
|
|
<i class="fas fa-plus me-2"></i>Deploy Stack
|
|
</button>
|
|
</div>
|
|
<div id="stacks-list" class="table-responsive">
|
|
<table class="table table-dark table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th>Stack Name</th>
|
|
<th>Services</th>
|
|
<th>Containers</th>
|
|
<th>Status</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="stacks-list-body">
|
|
<tr>
|
|
<td colspan="5" class="text-center text-muted">No stacks found. Deploy a stack to get started.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Deploy Stack Modal -->
|
|
<div class="modal fade" id="deploy-stack-modal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content bg-dark text-white">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Deploy Docker Compose Stack</h5>
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="deploy-stack-form">
|
|
<div class="mb-3">
|
|
<label for="stack-name" class="form-label">Stack Name</label>
|
|
<input type="text" class="form-control bg-dark text-white" id="stack-name" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="compose-content" class="form-label">Docker Compose YAML</label>
|
|
<textarea class="form-control bg-dark text-white" id="compose-content" rows="15" required placeholder="version: '3'
|
|
services:
|
|
web:
|
|
image: nginx
|
|
ports:
|
|
- '80:80'
|
|
db:
|
|
image: postgres
|
|
environment:
|
|
POSTGRES_PASSWORD: password"></textarea>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-primary" id="deploy-stack-btn">Deploy Stack</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="volumes-view" class="view hidden">
|
|
<div class="container-fluid">
|
|
<div class="page-header">
|
|
<div>
|
|
<h2><i class="fas fa-hard-drive"></i>Volumes</h2>
|
|
<p class="page-subtitle">Persistent storage volumes</p>
|
|
</div>
|
|
<div class="btn-group">
|
|
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createVolumeModal">
|
|
<i class="fas fa-plus me-2"></i>Create Volume
|
|
</button>
|
|
<button class="btn btn-outline-warning" type="button" onclick="window.pruneResource && window.pruneResource('volumes')">
|
|
<i class="fas fa-broom me-2"></i>Prune
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table class="table table-dark table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Driver</th>
|
|
<th>Mountpoint</th>
|
|
<th>Usage</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="volumes-list"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Fleet multi-host overview -->
|
|
<div id="fleet-view" class="view hidden">
|
|
<div class="container-fluid">
|
|
<div class="page-header">
|
|
<div>
|
|
<h2><i class="fas fa-server"></i>Fleet</h2>
|
|
<p class="page-subtitle">Concurrent multi-host health and Docker status</p>
|
|
</div>
|
|
<button class="btn btn-outline-primary" type="button" id="fleet-refresh-btn">
|
|
<i class="fas fa-sync me-2"></i>Refresh
|
|
</button>
|
|
</div>
|
|
<div id="fleet-cards" class="row g-3"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Access: peers, invites, vault -->
|
|
<div id="access-view" class="view hidden">
|
|
<div class="container-fluid">
|
|
<div class="page-header">
|
|
<div>
|
|
<h2><i class="fas fa-user-shield"></i>Access</h2>
|
|
<p class="page-subtitle">Peer invites, revoke list, registry vault</p>
|
|
</div>
|
|
</div>
|
|
<div class="row g-4">
|
|
<div class="col-lg-6">
|
|
<div class="card bg-dark border-secondary">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<span><i class="fas fa-users me-2"></i>Peers</span>
|
|
<button class="btn btn-sm btn-primary" id="access-invite-btn" type="button">
|
|
<i class="fas fa-ticket me-1"></i>Create invite
|
|
</button>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="access-peers-list" class="small text-muted">Connect as admin to manage peers.</div>
|
|
<hr>
|
|
<h6 class="text-muted">Active invites</h6>
|
|
<div id="access-invites-list" class="small"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-6">
|
|
<div class="card bg-dark border-secondary">
|
|
<div class="card-header"><i class="fas fa-key me-2"></i>Registry vault</div>
|
|
<div class="card-body">
|
|
<form id="vault-store-form" class="mb-3">
|
|
<div class="row g-2">
|
|
<div class="col-md-6">
|
|
<input class="form-control form-control-sm bg-dark text-white" name="username" placeholder="Username" required>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<input class="form-control form-control-sm bg-dark text-white" name="password" type="password" placeholder="Password" required>
|
|
</div>
|
|
<div class="col-12">
|
|
<input class="form-control form-control-sm bg-dark text-white" name="serveraddress" placeholder="Registry (default Docker Hub)">
|
|
</div>
|
|
<div class="col-12">
|
|
<button class="btn btn-sm btn-primary" type="submit">Store encrypted</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
<div id="access-vault-list" class="small"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Events -->
|
|
<div id="events-view" class="view hidden">
|
|
<div class="container-fluid">
|
|
<div class="page-header">
|
|
<div>
|
|
<h2><i class="fas fa-bolt"></i>Events</h2>
|
|
<p class="page-subtitle">Live Docker engine activity on the active peer</p>
|
|
</div>
|
|
<div class="page-header-actions">
|
|
<button class="btn btn-outline-primary" type="button" id="events-refresh-btn"><i class="fas fa-sync me-1"></i>Refresh</button>
|
|
<button class="btn btn-outline-secondary" type="button" id="events-pause-btn">Pause</button>
|
|
</div>
|
|
</div>
|
|
<div class="mb-2">
|
|
<input type="search" id="events-filter" class="form-control bg-dark text-white" placeholder="Filter events…">
|
|
</div>
|
|
<div id="events-full-list" class="list-group list-group-flush" style="font-family: var(--font-mono, monospace); font-size: 0.85rem;"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Host / engine -->
|
|
<div id="host-view" class="view hidden">
|
|
<div class="container-fluid">
|
|
<div class="page-header">
|
|
<div>
|
|
<h2><i class="fas fa-microchip"></i>Host</h2>
|
|
<p class="page-subtitle">Engine info, capacity, and process metrics</p>
|
|
</div>
|
|
<button class="btn btn-outline-primary" type="button" id="host-refresh-btn"><i class="fas fa-sync me-1"></i>Refresh</button>
|
|
</div>
|
|
<div class="row g-3">
|
|
<div class="col-lg-6">
|
|
<div class="settings-section">
|
|
<h3>Engine</h3>
|
|
<div id="host-engine-summary" class="small text-muted">Connect a peer to load host data.</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-6">
|
|
<div class="settings-section">
|
|
<h3>Metrics</h3>
|
|
<div id="host-metrics-summary" class="small text-muted">—</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-12">
|
|
<div class="settings-section">
|
|
<h3>Raw info</h3>
|
|
<pre id="host-info-raw" class="small mb-0" style="max-height: 20rem; overflow: auto; color: #94a3b8;"></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Settings -->
|
|
<div id="settings-view" class="view hidden">
|
|
<div class="container-fluid">
|
|
<div class="page-header">
|
|
<div>
|
|
<h2><i class="fas fa-gear"></i>Settings</h2>
|
|
<p class="page-subtitle">Client preferences and peer context</p>
|
|
</div>
|
|
</div>
|
|
<div class="settings-section">
|
|
<h3>Client</h3>
|
|
<div class="row g-3">
|
|
<div class="col-md-4">
|
|
<label class="form-label">Table density</label>
|
|
<select id="settings-density" class="form-select bg-dark text-white">
|
|
<option value="comfortable">Comfortable</option>
|
|
<option value="compact">Compact</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Confirm destructive actions</label>
|
|
<select id="settings-confirm" class="form-select bg-dark text-white">
|
|
<option value="1">Always (recommended)</option>
|
|
<option value="0">Skip (not recommended)</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Auto-refresh lists (seconds)</label>
|
|
<input type="number" id="settings-refresh" class="form-control bg-dark text-white" min="0" max="120" value="10">
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-primary btn-sm mt-3" id="settings-save-btn">Save preferences</button>
|
|
</div>
|
|
<div class="settings-section">
|
|
<h3>Active peer</h3>
|
|
<div id="settings-peer-info" class="small text-muted">Not connected</div>
|
|
</div>
|
|
<div class="settings-section">
|
|
<h3>Saved peers</h3>
|
|
<p class="small text-muted mb-2">
|
|
Clear all configured peers from this client. You will need to re-add public keys to connect again.
|
|
</p>
|
|
<button type="button" id="settings-reset-peers-btn" class="btn btn-outline-danger btn-sm">
|
|
<i class="fas fa-trash-can me-1"></i>Reset all peers
|
|
</button>
|
|
</div>
|
|
<div class="settings-section">
|
|
<h3>About</h3>
|
|
<p class="small text-muted mb-1">peardock — decentralized Docker control plane over HyperDHT.</p>
|
|
<p class="small text-muted mb-0">Keyboard: <kbd>Ctrl</kbd>+<kbd>K</kbd> command palette · <kbd>?</kbd> shortcuts</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="job-drawer" aria-live="polite"></div>
|
|
|
|
<!-- Create Network Modal (smart defaults) -->
|
|
<div class="modal fade" id="createNetworkSmartModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content bg-dark text-white">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Create network</h5>
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">Name <span class="pd-suggested" id="net-name-badge" style="display:none">Suggested</span></label>
|
|
<input type="text" id="smart-net-name" class="form-control bg-dark text-white" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Driver</label>
|
|
<select id="smart-net-driver" class="form-select bg-dark text-white">
|
|
<option value="bridge">bridge</option>
|
|
<option value="overlay">overlay</option>
|
|
<option value="macvlan">macvlan</option>
|
|
<option value="ipvlan">ipvlan</option>
|
|
<option value="host">host</option>
|
|
<option value="null">null</option>
|
|
</select>
|
|
</div>
|
|
<div class="row g-2">
|
|
<div class="col-md-7">
|
|
<label class="form-label">Subnet <span class="pd-suggested" id="net-subnet-badge">Suggested</span></label>
|
|
<input type="text" id="smart-net-subnet" class="form-control bg-dark text-white" placeholder="172.20.0.0/16">
|
|
</div>
|
|
<div class="col-md-5">
|
|
<label class="form-label">Gateway</label>
|
|
<input type="text" id="smart-net-gateway" class="form-control bg-dark text-white" placeholder="172.20.0.1">
|
|
</div>
|
|
</div>
|
|
<button type="button" class="btn btn-sm btn-outline-info mt-2" id="smart-net-resuggest">Regenerate free subnet</button>
|
|
<div class="form-text form-hint-suggested mt-2">IPAM is auto-detected from existing networks so ranges do not collide.</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-primary" id="smart-net-create-btn">Create</button>
|
|
</div>
|
|
</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-xl">
|
|
<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" style="max-height: 80vh; overflow-y: auto;">
|
|
<form id="duplicate-container-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="duplicate-container-name" class="form-label">Container Name <span class="text-danger">*</span></label>
|
|
<input type="text" id="duplicate-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="duplicate-image" class="form-label">Image <span class="text-danger">*</span></label>
|
|
<input type="text" id="duplicate-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="duplicate-command" class="form-label">Command Override</label>
|
|
<input type="text" id="duplicate-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="duplicate-entrypoint" class="form-label">Entrypoint Override</label>
|
|
<input type="text" id="duplicate-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="duplicate-workdir" class="form-label">Working Directory</label>
|
|
<input type="text" id="duplicate-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="duplicateAccordion">
|
|
<!-- 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="#duplicateNetworkingCollapse">
|
|
<i class="fas fa-network-wired me-2"></i> Networking
|
|
</button>
|
|
</h2>
|
|
<div id="duplicateNetworkingCollapse" class="accordion-collapse collapse" data-bs-parent="#duplicateAccordion">
|
|
<div class="accordion-body">
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<label for="duplicate-network-mode" class="form-label">Network Mode</label>
|
|
<select id="duplicate-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="duplicate-custom-network-container" style="display: none;">
|
|
<label for="duplicate-custom-network" class="form-label">Custom Network</label>
|
|
<input type="text" id="duplicate-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="duplicate-hostname" class="form-label">Hostname</label>
|
|
<input type="text" id="duplicate-hostname" class="form-control bg-dark text-white" placeholder="container-hostname">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="duplicate-domainname" class="form-label">Domain Name</label>
|
|
<input type="text" id="duplicate-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="duplicate-ports-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addDuplicatePortMapping()">
|
|
<i class="fas fa-plus"></i> Add Port
|
|
</button>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">DNS Servers</label>
|
|
<div id="duplicate-dns-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addDuplicateDnsServer()">
|
|
<i class="fas fa-plus"></i> Add DNS Server
|
|
</button>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Extra Hosts</label>
|
|
<div id="duplicate-extra-hosts-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addDuplicateExtraHost()">
|
|
<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="#duplicateVolumesCollapse">
|
|
<i class="fas fa-hdd me-2"></i> Volumes & Storage
|
|
</button>
|
|
</h2>
|
|
<div id="duplicateVolumesCollapse" class="accordion-collapse collapse" data-bs-parent="#duplicateAccordion">
|
|
<div class="accordion-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">Volume Mounts</label>
|
|
<div id="duplicate-volumes-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addDuplicateVolumeMount()">
|
|
<i class="fas fa-plus"></i> Add Volume
|
|
</button>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Tmpfs Mounts</label>
|
|
<div id="duplicate-tmpfs-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addDuplicateTmpfsMount()">
|
|
<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="#duplicateResourcesCollapse">
|
|
<i class="fas fa-server me-2"></i> Resources
|
|
</button>
|
|
</h2>
|
|
<div id="duplicateResourcesCollapse" class="accordion-collapse collapse" data-bs-parent="#duplicateAccordion">
|
|
<div class="accordion-body">
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<label for="duplicate-cpu-limit" class="form-label">CPU Limit: <strong id="duplicate-cpu-limit-val">Unlimited</strong></label>
|
|
<input type="range" class="form-range" id="duplicate-cpu-limit" min="0" max="16" step="0.1" value="0">
|
|
<small class="text-muted">0 = unlimited · cores</small>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="duplicate-memory-limit" class="form-label">Memory Limit: <strong id="duplicate-memory-limit-val">Unlimited</strong></label>
|
|
<input type="range" class="form-range" id="duplicate-memory-limit" min="0" max="16384" step="64" value="0">
|
|
<small class="text-muted">0 = unlimited · MB</small>
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-md-4">
|
|
<label for="duplicate-cpu-reservation" class="form-label">CPU Reservation</label>
|
|
<input type="number" id="duplicate-cpu-reservation" class="form-control bg-dark text-white" placeholder="1" step="0.1" min="0">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="duplicate-cpu-shares" class="form-label">CPU Shares</label>
|
|
<input type="number" id="duplicate-cpu-shares" class="form-control bg-dark text-white" placeholder="1024" min="0">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="duplicate-memory-reservation" class="form-label">Memory Reservation (MB)</label>
|
|
<input type="number" id="duplicate-memory-reservation" class="form-control bg-dark text-white" placeholder="256" min="0">
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-md-4">
|
|
<label for="duplicate-memory-swap" class="form-label">Memory Swap (MB)</label>
|
|
<input type="number" id="duplicate-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="duplicate-devices-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addDuplicateDeviceMapping()">
|
|
<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="#duplicateEnvLabelsCollapse">
|
|
<i class="fas fa-tags me-2"></i> Environment & Labels
|
|
</button>
|
|
</h2>
|
|
<div id="duplicateEnvLabelsCollapse" class="accordion-collapse collapse" data-bs-parent="#duplicateAccordion">
|
|
<div class="accordion-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">Environment Variables</label>
|
|
<div id="duplicate-env"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addDuplicateEnvVar()">
|
|
<i class="fas fa-plus"></i> Add Variable
|
|
</button>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Labels</label>
|
|
<div id="duplicate-labels-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addDuplicateLabel()">
|
|
<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="#duplicateSecurityCollapse">
|
|
<i class="fas fa-shield-alt me-2"></i> Security
|
|
</button>
|
|
</h2>
|
|
<div id="duplicateSecurityCollapse" class="accordion-collapse collapse" data-bs-parent="#duplicateAccordion">
|
|
<div class="accordion-body">
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<label for="duplicate-user" class="form-label">User/UID</label>
|
|
<input type="text" id="duplicate-user" class="form-control bg-dark text-white" placeholder="1000 or username">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="duplicate-group" class="form-label">Group/GID</label>
|
|
<input type="text" id="duplicate-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 form-switch">
|
|
<input class="form-check-input" type="checkbox" role="switch" id="duplicate-privileged">
|
|
<label class="form-check-label" for="duplicate-privileged">Privileged Mode</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-check form-switch">
|
|
<input class="form-check-input" type="checkbox" role="switch" id="duplicate-readonly-rootfs">
|
|
<label class="form-check-label" for="duplicate-readonly-rootfs">Read-only Root Filesystem</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Capabilities</label>
|
|
<div id="duplicate-capabilities-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addDuplicateCapability()">
|
|
<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="duplicate-security-opts-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addDuplicateSecurityOpt()">
|
|
<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="#duplicateRuntimeCollapse">
|
|
<i class="fas fa-play-circle me-2"></i> Runtime & Behavior
|
|
</button>
|
|
</h2>
|
|
<div id="duplicateRuntimeCollapse" class="accordion-collapse collapse" data-bs-parent="#duplicateAccordion">
|
|
<div class="accordion-body">
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<label for="duplicate-restart-policy" class="form-label">Restart Policy</label>
|
|
<select id="duplicate-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="duplicate-restart-max-retries" class="form-label">Max Retries (on-failure)</label>
|
|
<input type="number" id="duplicate-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 form-switch">
|
|
<input class="form-check-input" type="checkbox" role="switch" id="duplicate-auto-remove">
|
|
<label class="form-check-label" for="duplicate-auto-remove">Auto Remove</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-check form-switch">
|
|
<input class="form-check-input" type="checkbox" role="switch" id="duplicate-tty">
|
|
<label class="form-check-label" for="duplicate-tty">TTY</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-check form-switch">
|
|
<input class="form-check-input" type="checkbox" role="switch" id="duplicate-stdin-open">
|
|
<label class="form-check-label" for="duplicate-stdin-open">Interactive (Stdin Open)</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<div class="form-check form-switch">
|
|
<input class="form-check-input" type="checkbox" role="switch" id="duplicate-detach" checked>
|
|
<label class="form-check-label" for="duplicate-detach">Detach Mode</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-check form-switch">
|
|
<input class="form-check-input" type="checkbox" role="switch" id="duplicate-init">
|
|
<label class="form-check-label" for="duplicate-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="#duplicateHealthCollapse">
|
|
<i class="fas fa-heartbeat me-2"></i> Health & Logging
|
|
</button>
|
|
</h2>
|
|
<div id="duplicateHealthCollapse" class="accordion-collapse collapse" data-bs-parent="#duplicateAccordion">
|
|
<div class="accordion-body">
|
|
<div class="mb-3">
|
|
<label for="duplicate-health-cmd" class="form-label">Health Check Command</label>
|
|
<input type="text" id="duplicate-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="duplicate-health-interval" class="form-label">Interval (s)</label>
|
|
<input type="number" id="duplicate-health-interval" class="form-control bg-dark text-white" placeholder="30" min="1">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label for="duplicate-health-timeout" class="form-label">Timeout (s)</label>
|
|
<input type="number" id="duplicate-health-timeout" class="form-control bg-dark text-white" placeholder="10" min="1">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label for="duplicate-health-retries" class="form-label">Retries</label>
|
|
<input type="number" id="duplicate-health-retries" class="form-control bg-dark text-white" placeholder="3" min="0">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label for="duplicate-health-start-period" class="form-label">Start Period (s)</label>
|
|
<input type="number" id="duplicate-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="duplicate-log-driver" class="form-label">Logging Driver</label>
|
|
<select id="duplicate-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="duplicate-log-opts-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addDuplicateLogOpt()">
|
|
<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="#duplicateAdvancedCollapse">
|
|
<i class="fas fa-cogs me-2"></i> Advanced
|
|
</button>
|
|
</h2>
|
|
<div id="duplicateAdvancedCollapse" class="accordion-collapse collapse" data-bs-parent="#duplicateAccordion">
|
|
<div class="accordion-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">Sysctls</label>
|
|
<div id="duplicate-sysctls-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addDuplicateSysctl()">
|
|
<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="duplicate-ulimits-container"></div>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" onclick="addDuplicateUlimit()">
|
|
<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 form-switch">
|
|
<input class="form-check-input" type="checkbox" role="switch" id="duplicate-oom-kill-disable">
|
|
<label class="form-check-label" for="duplicate-oom-kill-disable">Disable OOM Killer</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="duplicate-pids-limit" class="form-label">PIDs Limit</label>
|
|
<input type="number" id="duplicate-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="duplicate-shm-size" class="form-label">Shared Memory Size (MB)</label>
|
|
<input type="number" id="duplicate-shm-size" class="form-control bg-dark text-white" placeholder="64" min="0">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</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-clone"></i> Duplicate Container
|
|
</button>
|
|
</div>
|
|
</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>
|
|
<!-- Terminal Toolbar -->
|
|
<div class="terminal-toolbar" id="modal-terminal-toolbar">
|
|
<div class="terminal-toolbar-left">
|
|
<div class="terminal-controls-group">
|
|
<button class="terminal-control-btn" id="modal-terminal-font-decrease" title="Decrease font size">
|
|
<i class="fas fa-minus"></i>
|
|
</button>
|
|
<span class="terminal-font-size" id="modal-terminal-font-size-display">14</span>
|
|
<button class="terminal-control-btn" id="modal-terminal-font-increase" title="Increase font size">
|
|
<i class="fas fa-plus"></i>
|
|
</button>
|
|
<button class="terminal-control-btn" id="modal-terminal-font-reset" title="Reset font size">
|
|
<i class="fas fa-undo"></i>
|
|
</button>
|
|
</div>
|
|
<div class="terminal-controls-divider"></div>
|
|
<div class="terminal-controls-group">
|
|
<button class="terminal-control-btn" id="modal-terminal-copy-btn" title="Copy selection">
|
|
<i class="fas fa-copy"></i> Copy
|
|
</button>
|
|
<button class="terminal-control-btn" id="modal-terminal-clear-btn" title="Clear terminal">
|
|
<i class="fas fa-trash"></i> Clear
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="terminal-toolbar-right">
|
|
<div class="terminal-controls-group">
|
|
<select class="terminal-theme-select" id="modal-terminal-theme-select" title="Select theme">
|
|
<option value="dark">Dark</option>
|
|
<option value="light">Light</option>
|
|
<option value="solarized-dark">Solarized Dark</option>
|
|
<option value="solarized-light">Solarized Light</option>
|
|
<option value="monokai">Monokai</option>
|
|
</select>
|
|
</div>
|
|
</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>
|
|
|
|
<!-- Add Peer / Connection Modal -->
|
|
<div class="modal fade" id="addConnectionModal" tabindex="-1" aria-labelledby="add-connection-modal-title" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content bg-dark text-white">
|
|
<div class="modal-header border-secondary">
|
|
<h5 class="modal-title" id="add-connection-modal-title">
|
|
<i class="fas fa-plug me-2"></i>Add peer
|
|
</h5>
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<form id="add-connection-form" autocomplete="off">
|
|
<div class="modal-body">
|
|
<p class="text-muted small mb-3">
|
|
Paste the server public key printed when you run <code>npm run server</code>. Optionally include an invite token if the host requires one.
|
|
</p>
|
|
<div class="mb-3">
|
|
<label for="new-connection-topic" class="form-label">Server public key</label>
|
|
<input type="text" id="new-connection-topic" class="form-control font-monospace" placeholder="64 hex characters…" spellcheck="false" required autocomplete="off">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="new-connection-invite" class="form-label">Invite token <span class="text-muted">(optional)</span></label>
|
|
<input type="text" id="new-connection-invite" class="form-control font-monospace" placeholder="Invite token" spellcheck="false" autocomplete="off">
|
|
</div>
|
|
<div class="mb-0">
|
|
<label for="new-connection-alias" class="form-label">Alias <span class="text-muted">(optional)</span></label>
|
|
<input type="text" id="new-connection-alias" class="form-control" placeholder="e.g. prod-host" spellcheck="false" maxlength="64" autocomplete="off">
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer border-secondary">
|
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-plug me-1"></i> Connect
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</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>
|
|
<!-- Docker Terminal Toolbar -->
|
|
<div class="terminal-toolbar" id="docker-terminal-toolbar">
|
|
<div class="terminal-toolbar-left">
|
|
<div class="terminal-controls-group">
|
|
<button class="terminal-control-btn" id="docker-terminal-font-decrease" title="Decrease font size">
|
|
<i class="fas fa-minus"></i>
|
|
</button>
|
|
<span class="terminal-font-size" id="docker-terminal-font-size-display">14</span>
|
|
<button class="terminal-control-btn" id="docker-terminal-font-increase" title="Increase font size">
|
|
<i class="fas fa-plus"></i>
|
|
</button>
|
|
<button class="terminal-control-btn" id="docker-terminal-font-reset" title="Reset font size">
|
|
<i class="fas fa-undo"></i>
|
|
</button>
|
|
</div>
|
|
<div class="terminal-controls-divider"></div>
|
|
<div class="terminal-controls-group">
|
|
<button class="terminal-control-btn" id="docker-terminal-copy-btn" title="Copy selection">
|
|
<i class="fas fa-copy"></i> Copy
|
|
</button>
|
|
<button class="terminal-control-btn" id="docker-terminal-clear-btn" title="Clear terminal">
|
|
<i class="fas fa-trash"></i> Clear
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="terminal-toolbar-right">
|
|
<div class="terminal-controls-group">
|
|
<select class="terminal-theme-select" id="docker-terminal-theme-select" title="Select theme">
|
|
<option value="dark">Dark</option>
|
|
<option value="light">Light</option>
|
|
<option value="solarized-dark">Solarized Dark</option>
|
|
<option value="solarized-light">Solarized Light</option>
|
|
<option value="monokai">Monokai</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-body" style="padding: 0;">
|
|
<div id="docker-terminal-container" style="height: 500px;"></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>
|
|
|
|
<!-- Container Inspect Modal -->
|
|
<div class="modal fade" id="containerInspectModal" tabindex="-1" aria-labelledby="containerInspectModalLabel" 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="containerInspectModalLabel">
|
|
<i class="fas fa-info-circle me-2"></i>Container Information
|
|
</h5>
|
|
<div class="d-flex gap-2">
|
|
<button type="button" class="btn btn-sm btn-outline-secondary" id="toggle-json-view">
|
|
<i class="fas fa-code"></i> Raw JSON
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-outline-primary" id="copy-json-btn">
|
|
<i class="fas fa-copy"></i> Copy JSON
|
|
</button>
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
</div>
|
|
<div class="modal-body" style="max-height: 80vh; overflow-y: auto;">
|
|
<!-- Formatted View -->
|
|
<div id="inspect-formatted-view">
|
|
<div class="accordion accordion-flush" id="inspectAccordion">
|
|
<!-- Overview Section -->
|
|
<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="#inspectOverviewCollapse">
|
|
<i class="fas fa-info-circle me-2"></i> Overview
|
|
</button>
|
|
</h2>
|
|
<div id="inspectOverviewCollapse" class="accordion-collapse collapse" data-bs-parent="#inspectAccordion">
|
|
<div class="accordion-body" id="inspect-overview-content">
|
|
<!-- Content will be populated by JavaScript -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Configuration Section -->
|
|
<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="#inspectConfigCollapse">
|
|
<i class="fas fa-cog me-2"></i> Configuration
|
|
</button>
|
|
</h2>
|
|
<div id="inspectConfigCollapse" class="accordion-collapse collapse" data-bs-parent="#inspectAccordion">
|
|
<div class="accordion-body" id="inspect-config-content">
|
|
<!-- Content will be populated by JavaScript -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Networking Section -->
|
|
<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="#inspectNetworkingCollapse">
|
|
<i class="fas fa-network-wired me-2"></i> Networking
|
|
</button>
|
|
</h2>
|
|
<div id="inspectNetworkingCollapse" class="accordion-collapse collapse" data-bs-parent="#inspectAccordion">
|
|
<div class="accordion-body" id="inspect-networking-content">
|
|
<!-- Content will be populated by JavaScript -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Storage Section -->
|
|
<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="#inspectStorageCollapse">
|
|
<i class="fas fa-hdd me-2"></i> Storage
|
|
</button>
|
|
</h2>
|
|
<div id="inspectStorageCollapse" class="accordion-collapse collapse" data-bs-parent="#inspectAccordion">
|
|
<div class="accordion-body" id="inspect-storage-content">
|
|
<!-- Content will be populated by JavaScript -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Resources Section -->
|
|
<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="#inspectResourcesCollapse">
|
|
<i class="fas fa-server me-2"></i> Resources
|
|
</button>
|
|
</h2>
|
|
<div id="inspectResourcesCollapse" class="accordion-collapse collapse" data-bs-parent="#inspectAccordion">
|
|
<div class="accordion-body" id="inspect-resources-content">
|
|
<!-- Content will be populated by JavaScript -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Security Section -->
|
|
<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="#inspectSecurityCollapse">
|
|
<i class="fas fa-shield-alt me-2"></i> Security
|
|
</button>
|
|
</h2>
|
|
<div id="inspectSecurityCollapse" class="accordion-collapse collapse" data-bs-parent="#inspectAccordion">
|
|
<div class="accordion-body" id="inspect-security-content">
|
|
<!-- Content will be populated by JavaScript -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Runtime Section -->
|
|
<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="#inspectRuntimeCollapse">
|
|
<i class="fas fa-play-circle me-2"></i> Runtime & Behavior
|
|
</button>
|
|
</h2>
|
|
<div id="inspectRuntimeCollapse" class="accordion-collapse collapse" data-bs-parent="#inspectAccordion">
|
|
<div class="accordion-body" id="inspect-runtime-content">
|
|
<!-- Content will be populated by JavaScript -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Health & Logging Section -->
|
|
<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="#inspectHealthCollapse">
|
|
<i class="fas fa-heartbeat me-2"></i> Health & Logging
|
|
</button>
|
|
</h2>
|
|
<div id="inspectHealthCollapse" class="accordion-collapse collapse" data-bs-parent="#inspectAccordion">
|
|
<div class="accordion-body" id="inspect-health-content">
|
|
<!-- Content will be populated by JavaScript -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Labels & Metadata Section -->
|
|
<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="#inspectLabelsCollapse">
|
|
<i class="fas fa-tags me-2"></i> Labels & Metadata
|
|
</button>
|
|
</h2>
|
|
<div id="inspectLabelsCollapse" class="accordion-collapse collapse" data-bs-parent="#inspectAccordion">
|
|
<div class="accordion-body" id="inspect-labels-content">
|
|
<!-- Content will be populated by JavaScript -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Raw JSON View -->
|
|
<div id="inspect-json-view" style="display: none;">
|
|
<pre id="inspect-json-content" class="bg-dark text-white p-3 rounded" style="max-height: 70vh; overflow-y: auto; font-size: 12px; font-family: 'Courier New', monospace; white-space: pre-wrap; word-wrap: break-word;"></pre>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Image Inspect Modal -->
|
|
<div class="modal fade" id="imageInspectModal" tabindex="-1" aria-labelledby="imageInspectModalLabel" 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="imageInspectModalLabel">
|
|
<i class="fas fa-info-circle me-2"></i>Image Information
|
|
</h5>
|
|
<div class="d-flex gap-2">
|
|
<button type="button" class="btn btn-sm btn-outline-secondary" id="toggle-image-json-view">
|
|
<i class="fas fa-code"></i> Raw JSON
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-outline-primary" id="copy-image-json-btn">
|
|
<i class="fas fa-copy"></i> Copy JSON
|
|
</button>
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
</div>
|
|
<div class="modal-body" style="max-height: 80vh; overflow-y: auto;">
|
|
<div id="image-inspect-formatted-view">
|
|
<div class="accordion accordion-flush" id="imageInspectAccordion">
|
|
<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="#imageOverviewCollapse">
|
|
<i class="fas fa-info-circle me-2"></i> Overview
|
|
</button>
|
|
</h2>
|
|
<div id="imageOverviewCollapse" class="accordion-collapse collapse" data-bs-parent="#imageInspectAccordion">
|
|
<div class="accordion-body" id="image-overview-content"></div>
|
|
</div>
|
|
</div>
|
|
<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="#imageConfigCollapse">
|
|
<i class="fas fa-cog me-2"></i> Configuration
|
|
</button>
|
|
</h2>
|
|
<div id="imageConfigCollapse" class="accordion-collapse collapse" data-bs-parent="#imageInspectAccordion">
|
|
<div class="accordion-body" id="image-config-content"></div>
|
|
</div>
|
|
</div>
|
|
<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="#imageHistoryCollapse">
|
|
<i class="fas fa-history me-2"></i> History & Layers
|
|
</button>
|
|
</h2>
|
|
<div id="imageHistoryCollapse" class="accordion-collapse collapse" data-bs-parent="#imageInspectAccordion">
|
|
<div class="accordion-body" id="image-history-content"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="image-inspect-json-view" style="display: none;">
|
|
<pre id="image-inspect-json-content" class="bg-dark text-white p-3 rounded" style="max-height: 70vh; overflow-y: auto; font-size: 12px; font-family: 'Courier New', monospace; white-space: pre-wrap; word-wrap: break-word;"></pre>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Network Inspect Modal -->
|
|
<div class="modal fade" id="networkInspectModal" tabindex="-1" aria-labelledby="networkInspectModalLabel" 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="networkInspectModalLabel">
|
|
<i class="fas fa-info-circle me-2"></i>Network Information
|
|
</h5>
|
|
<div class="d-flex gap-2">
|
|
<button type="button" class="btn btn-sm btn-outline-secondary" id="toggle-network-json-view">
|
|
<i class="fas fa-code"></i> Raw JSON
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-outline-primary" id="copy-network-json-btn">
|
|
<i class="fas fa-copy"></i> Copy JSON
|
|
</button>
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
</div>
|
|
<div class="modal-body" style="max-height: 80vh; overflow-y: auto;">
|
|
<div id="network-inspect-formatted-view">
|
|
<div class="accordion accordion-flush" id="networkInspectAccordion">
|
|
<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="#networkOverviewCollapse">
|
|
<i class="fas fa-info-circle me-2"></i> Overview
|
|
</button>
|
|
</h2>
|
|
<div id="networkOverviewCollapse" class="accordion-collapse collapse" data-bs-parent="#networkInspectAccordion">
|
|
<div class="accordion-body" id="network-overview-content"></div>
|
|
</div>
|
|
</div>
|
|
<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="#networkConfigCollapse">
|
|
<i class="fas fa-cog me-2"></i> Configuration
|
|
</button>
|
|
</h2>
|
|
<div id="networkConfigCollapse" class="accordion-collapse collapse" data-bs-parent="#networkInspectAccordion">
|
|
<div class="accordion-body" id="network-config-content"></div>
|
|
</div>
|
|
</div>
|
|
<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="#networkContainersCollapse">
|
|
<i class="fas fa-box me-2"></i> Connected Containers
|
|
</button>
|
|
</h2>
|
|
<div id="networkContainersCollapse" class="accordion-collapse collapse" data-bs-parent="#networkInspectAccordion">
|
|
<div class="accordion-body" id="network-containers-content"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="network-inspect-json-view" style="display: none;">
|
|
<pre id="network-inspect-json-content" class="bg-dark text-white p-3 rounded" style="max-height: 70vh; overflow-y: auto; font-size: 12px; font-family: 'Courier New', monospace; white-space: pre-wrap; word-wrap: break-word;"></pre>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Volume Inspect Modal -->
|
|
<div class="modal fade" id="volumeInspectModal" tabindex="-1" aria-labelledby="volumeInspectModalLabel" 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="volumeInspectModalLabel">
|
|
<i class="fas fa-info-circle me-2"></i>Volume Information
|
|
</h5>
|
|
<div class="d-flex gap-2">
|
|
<button type="button" class="btn btn-sm btn-outline-secondary" id="toggle-volume-json-view">
|
|
<i class="fas fa-code"></i> Raw JSON
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-outline-primary" id="copy-volume-json-btn">
|
|
<i class="fas fa-copy"></i> Copy JSON
|
|
</button>
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
</div>
|
|
<div class="modal-body" style="max-height: 80vh; overflow-y: auto;">
|
|
<div id="volume-inspect-formatted-view">
|
|
<div class="accordion accordion-flush" id="volumeInspectAccordion">
|
|
<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="#volumeOverviewCollapse">
|
|
<i class="fas fa-info-circle me-2"></i> Overview
|
|
</button>
|
|
</h2>
|
|
<div id="volumeOverviewCollapse" class="accordion-collapse collapse" data-bs-parent="#volumeInspectAccordion">
|
|
<div class="accordion-body" id="volume-overview-content"></div>
|
|
</div>
|
|
</div>
|
|
<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="#volumeConfigCollapse">
|
|
<i class="fas fa-cog me-2"></i> Configuration
|
|
</button>
|
|
</h2>
|
|
<div id="volumeConfigCollapse" class="accordion-collapse collapse" data-bs-parent="#volumeInspectAccordion">
|
|
<div class="accordion-body" id="volume-config-content"></div>
|
|
</div>
|
|
</div>
|
|
<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="#volumeUsageCollapse">
|
|
<i class="fas fa-box me-2"></i> Usage
|
|
</button>
|
|
</h2>
|
|
<div id="volumeUsageCollapse" class="accordion-collapse collapse" data-bs-parent="#volumeInspectAccordion">
|
|
<div class="accordion-body" id="volume-usage-content"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="volume-inspect-json-view" style="display: none;">
|
|
<pre id="volume-inspect-json-content" class="bg-dark text-white p-3 rounded" style="max-height: 70vh; overflow-y: auto; font-size: 12px; font-family: 'Courier New', monospace; white-space: pre-wrap; word-wrap: break-word;"></pre>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stack Inspect Modal -->
|
|
<div class="modal fade" id="stackInspectModal" tabindex="-1" aria-labelledby="stackInspectModalLabel" 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="stackInspectModalLabel">
|
|
<i class="fas fa-info-circle me-2"></i>Stack Information
|
|
</h5>
|
|
<div class="d-flex gap-2">
|
|
<button type="button" class="btn btn-sm btn-outline-secondary" id="toggle-stack-json-view">
|
|
<i class="fas fa-code"></i> Raw JSON
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-outline-primary" id="copy-stack-json-btn">
|
|
<i class="fas fa-copy"></i> Copy JSON
|
|
</button>
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
</div>
|
|
<div class="modal-body" style="max-height: 80vh; overflow-y: auto;">
|
|
<div id="stack-inspect-formatted-view">
|
|
<div class="accordion accordion-flush" id="stackInspectAccordion">
|
|
<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="#stackOverviewCollapse">
|
|
<i class="fas fa-info-circle me-2"></i> Overview
|
|
</button>
|
|
</h2>
|
|
<div id="stackOverviewCollapse" class="accordion-collapse collapse" data-bs-parent="#stackInspectAccordion">
|
|
<div class="accordion-body" id="stack-overview-content"></div>
|
|
</div>
|
|
</div>
|
|
<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="#stackServicesCollapse">
|
|
<i class="fas fa-cogs me-2"></i> Services
|
|
</button>
|
|
</h2>
|
|
<div id="stackServicesCollapse" class="accordion-collapse collapse" data-bs-parent="#stackInspectAccordion">
|
|
<div class="accordion-body" id="stack-services-content"></div>
|
|
</div>
|
|
</div>
|
|
<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="#stackContainersCollapse">
|
|
<i class="fas fa-box me-2"></i> Containers
|
|
</button>
|
|
</h2>
|
|
<div id="stackContainersCollapse" class="accordion-collapse collapse" data-bs-parent="#stackInspectAccordion">
|
|
<div class="accordion-body" id="stack-containers-content"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="stack-inspect-json-view" style="display: none;">
|
|
<pre id="stack-inspect-json-content" class="bg-dark text-white p-3 rounded" style="max-height: 70vh; overflow-y: auto; font-size: 12px; font-family: 'Courier New', monospace; white-space: pre-wrap; word-wrap: break-word;"></pre>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
|
</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>
|
|
</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>
|
|
</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>
|
|
<div>
|
|
<button type="button" class="btn btn-outline-info me-2" onclick="saveCurrentTemplate()">
|
|
<i class="fas fa-save"></i> Save as Template
|
|
</button>
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-rocket"></i> Deploy Container
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- File Browser Modal -->
|
|
<div class="modal fade" id="fileBrowserModal" tabindex="-1" aria-labelledby="fileBrowserModalLabel" 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="fileBrowserModalLabel"><i class="fas fa-folder-open me-2"></i>Browse Directory</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="fileBrowserBreadcrumb" class="mb-3"></div>
|
|
<div id="fileBrowserContent" style="min-height: 300px; max-height: 400px; overflow-y: auto; border: 1px solid var(--border-color); border-radius: 4px; padding: 1rem;">
|
|
<div class="text-center p-4 text-muted">
|
|
<i class="fas fa-spinner fa-spin"></i> Loading...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-primary" onclick="selectDirectoryForVolume()">
|
|
<i class="fas fa-check"></i> Select Directory
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pull Image Modal -->
|
|
<div class="modal fade" id="pullImageModal" tabindex="-1" aria-labelledby="pullImageModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content bg-dark text-white">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="pullImageModalLabel"><i class="fas fa-download me-2"></i>Pull Image</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="pull-image-form">
|
|
<div class="mb-3">
|
|
<label for="pull-image-name" class="form-label">Image Name</label>
|
|
<input type="text" id="pull-image-name" class="form-control bg-dark text-white" placeholder="nginx:latest" required>
|
|
<small class="text-muted">Enter the full image name with tag (e.g., nginx:latest, ubuntu:20.04)</small>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-primary" onclick="pullImage()">
|
|
<i class="fas fa-download me-2"></i>Pull Image
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Generic Confirmation Modal -->
|
|
<div class="modal fade" id="confirmModal" tabindex="-1" aria-labelledby="confirmModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content bg-dark text-white">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="confirmModalLabel">Confirm Action</h5>
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p id="confirmModalMessage">Are you sure?</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-primary" id="confirmModalBtn">Confirm</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Rename Container Modal -->
|
|
<div class="modal fade" id="renameContainerModal" tabindex="-1" aria-labelledby="renameContainerModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content bg-dark text-white">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="renameContainerModalLabel">Rename 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="renameContainerForm">
|
|
<div class="mb-3">
|
|
<label for="new-container-name" class="form-label">New Container Name</label>
|
|
<input type="text" class="form-control bg-dark text-white" id="new-container-name" required>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-primary" id="confirm-rename-btn">Rename</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Commit Container Modal -->
|
|
<div class="modal fade" id="commitContainerModal" tabindex="-1" aria-labelledby="commitContainerModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content bg-dark text-white">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="commitContainerModalLabel">Commit 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="commitContainerForm">
|
|
<div class="mb-3">
|
|
<label for="commit-repo" class="form-label">Repository Name</label>
|
|
<input type="text" class="form-control bg-dark text-white" id="commit-repo" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="commit-tag" class="form-label">Tag</label>
|
|
<input type="text" class="form-control bg-dark text-white" id="commit-tag" value="latest">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="commit-message" class="form-label">Commit Message (Optional)</label>
|
|
<textarea class="form-control bg-dark text-white" id="commit-message" rows="3"></textarea>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-primary" id="confirm-commit-btn">Commit</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Exec Container Modal -->
|
|
<div class="modal fade" id="execContainerModal" tabindex="-1" aria-labelledby="execContainerModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content bg-dark text-white">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="execContainerModalLabel">Execute Command in 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="execContainerForm">
|
|
<div class="mb-3">
|
|
<label for="exec-command" class="form-label">Command</label>
|
|
<input type="text" class="form-control bg-dark text-white font-monospace" id="exec-command" value="/bin/sh" required>
|
|
<small class="text-muted">Enter command to execute (e.g., /bin/sh, /bin/bash, ls -la)</small>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-primary" id="confirm-exec-btn">Execute</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tag Image Modal -->
|
|
<div class="modal fade" id="tagImageModal" tabindex="-1" aria-labelledby="tagImageModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content bg-dark text-white">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="tagImageModalLabel">Tag Image</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="tagImageForm">
|
|
<div class="mb-3">
|
|
<label for="tag-repo" class="form-label">Repository Name</label>
|
|
<input type="text" class="form-control bg-dark text-white" id="tag-repo" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="tag-tag" class="form-label">Tag</label>
|
|
<input type="text" class="form-control bg-dark text-white" id="tag-tag" value="latest">
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-primary" id="confirm-tag-btn">Tag</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Connect Container to Network Modal -->
|
|
<div class="modal fade" id="connectNetworkModal" tabindex="-1" aria-labelledby="connectNetworkModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content bg-dark text-white">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="connectNetworkModalLabel">Connect Container to Network</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="connectNetworkForm">
|
|
<div class="mb-3">
|
|
<label for="connect-container-select" class="form-label">Select Container</label>
|
|
<select class="form-select bg-dark text-white" id="connect-container-select" required>
|
|
<option value="">Loading containers...</option>
|
|
</select>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-primary" id="confirm-connect-btn">Connect</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Create Network Modal -->
|
|
<div class="modal fade" id="createNetworkModal" tabindex="-1" aria-labelledby="createNetworkModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content bg-dark text-white">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="createNetworkModalLabel"><i class="fas fa-plus me-2"></i>Create Network</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="create-network-form">
|
|
<div class="mb-3">
|
|
<label for="network-name" class="form-label">Network Name</label>
|
|
<input type="text" id="network-name" class="form-control bg-dark text-white" placeholder="my-network" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="network-driver" class="form-label">Driver</label>
|
|
<select id="network-driver" class="form-select bg-dark text-white">
|
|
<option value="bridge">Bridge</option>
|
|
<option value="overlay">Overlay</option>
|
|
<option value="macvlan">Macvlan</option>
|
|
<option value="host">Host</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="network-subnet" class="form-label">Subnet (Optional)</label>
|
|
<input type="text" id="network-subnet" class="form-control bg-dark text-white" placeholder="172.20.0.0/16">
|
|
<small class="text-muted">CIDR notation (e.g., 172.20.0.0/16)</small>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-primary" onclick="createNetwork()">
|
|
<i class="fas fa-plus me-2"></i>Create Network
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Create Volume Modal -->
|
|
<div class="modal fade" id="createVolumeModal" tabindex="-1" aria-labelledby="createVolumeModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content bg-dark text-white">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="createVolumeModalLabel"><i class="fas fa-plus me-2"></i>Create Volume</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="create-volume-form">
|
|
<div class="mb-3">
|
|
<label for="volume-name" class="form-label">Volume Name</label>
|
|
<input type="text" id="volume-name" class="form-control bg-dark text-white" placeholder="my-volume" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="volume-driver" class="form-label">Driver (Optional)</label>
|
|
<input type="text" id="volume-driver" class="form-control bg-dark text-white" placeholder="local">
|
|
<small class="text-muted">Leave empty for default local driver</small>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-primary" onclick="createVolume()">
|
|
<i class="fas fa-plus me-2"></i>Create Volume
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Legacy Alert Container (kept for backward compatibility, will be hidden) -->
|
|
<div id="alert-container" class="position-fixed top-0 start-50 translate-middle-x mt-3"
|
|
style="z-index: 1051; max-width: 90%; display: none;"></div>
|
|
|
|
|
|
<!-- xterm.js and addons loaded via script tags for UMD compatibility -->
|
|
<!-- Scripts load synchronously before modules -->
|
|
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/xterm.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/xterm-addon-fit.js"></script>
|
|
<script>
|
|
// Verify xterm and FitAddon are loaded immediately after scripts
|
|
if (typeof window.Terminal === 'undefined') {
|
|
console.error('xterm.js failed to load');
|
|
} else {
|
|
console.log('xterm.js loaded successfully');
|
|
}
|
|
if (typeof window.FitAddon === 'undefined') {
|
|
console.error('xterm-addon-fit failed to load');
|
|
// Check for alternative namespaces
|
|
console.log('Checking alternative namespaces...');
|
|
console.log('window.xterm:', window.xterm);
|
|
console.log('window.xtermAddonFit:', window.xtermAddonFit);
|
|
console.log('Available window properties:', Object.keys(window).filter(k => k.toLowerCase().includes('fit') || k.toLowerCase().includes('xterm')));
|
|
} else {
|
|
console.log('xterm-addon-fit loaded successfully');
|
|
console.log('FitAddon type:', typeof window.FitAddon);
|
|
console.log('FitAddon is function:', typeof window.FitAddon === 'function');
|
|
console.log('FitAddon value:', window.FitAddon);
|
|
// Test if it's a constructor (check for nested FitAddon property)
|
|
try {
|
|
const FitAddonConstructor = window.FitAddon.FitAddon || window.FitAddon;
|
|
if (typeof FitAddonConstructor === 'function') {
|
|
const test = new FitAddonConstructor();
|
|
console.log('FitAddon constructor test: SUCCESS (using', window.FitAddon.FitAddon ? 'nested' : 'direct', 'property)');
|
|
} else {
|
|
console.error('FitAddon constructor test: FAILED - not a function');
|
|
}
|
|
} catch (e) {
|
|
console.error('FitAddon constructor test: FAILED', e);
|
|
}
|
|
}
|
|
</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> |