Files
stackpilot/index.html

212 lines
9.6 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>StackPilot — Docker Stack Builder</title>
<link rel="icon" href="/favicon.svg" />
<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 rel="stylesheet" href="/src/style.css" />
</head>
<body>
<header class="header">
<h1>🚀 StackPilot</h1>
<p class="subtitle">Build your self-hosted Docker stack in minutes</p>
<nav class="mode-nav">
<button class="mode-btn active" data-mode="builder">Stack Builder</button>
<button class="mode-btn" data-mode="analyser">Compose Analyser</button>
<button class="mode-btn" data-mode="gallery">Community Stacks</button>
</nav>
<div class="auth-bar" id="auth-bar">
<button class="auth-btn" id="auth-btn">Sign In</button>
</div>
</header>
<main class="container">
<!-- Step 1: Global Config -->
<section class="step" id="step-config">
<h2>1. Configure Paths</h2>
<div class="os-selector">
<label class="os-option">
<input type="radio" name="os" value="linux" checked />
<span class="os-label">🐧 Linux</span>
</label>
<label class="os-option">
<input type="radio" name="os" value="macos" />
<span class="os-label">🍎 macOS</span>
</label>
<label class="os-option">
<input type="radio" name="os" value="windows" />
<span class="os-label">🪟 Windows</span>
</label>
</div>
<div class="config-grid">
<label>
<span>Config path</span>
<input type="text" id="config-path" value="/opt/stackpilot/config" placeholder="/opt/stackpilot/config" />
</label>
<label>
<span>Media path</span>
<input type="text" id="media-path" value="/mnt/media" placeholder="/mnt/media" />
</label>
<label>
<span>Downloads path</span>
<input type="text" id="downloads-path" value="/mnt/downloads" placeholder="/mnt/downloads" />
</label>
<label>
<span>Timezone</span>
<input type="text" id="timezone" value="Europe/Dublin" placeholder="Europe/London" />
</label>
</div>
</section>
<!-- Step 2: App Selection -->
<section class="step" id="step-apps">
<h2>2. Select Apps</h2>
<div class="search-bar">
<input type="text" id="app-search" placeholder="🔍 Search 186 apps..." />
</div>
<div class="presets-bar">
<span class="presets-label">Quick stacks:</span>
<button class="preset-btn" data-preset="media">🎬 Media Server</button>
<button class="preset-btn" data-preset="arr">📡 Full Arr Suite</button>
<button class="preset-btn" data-preset="ai">🤖 AI Lab</button>
<button class="preset-btn" data-preset="dev">💻 Dev Stack</button>
<button class="preset-btn" data-preset="homelab">🏠 Homelab Essentials</button>
<button class="preset-btn" data-preset="gaming">🎮 Game Servers</button>
</div>
<div id="dependency-warnings" class="dependency-warnings"></div>
<div id="port-conflicts" class="port-conflicts"></div>
<div id="app-grid"></div>
</section>
<!-- Step 3: Tailscale Remote Access -->
<section class="step" id="step-tailscale">
<h2>3. Remote Access (Optional)</h2>
<div class="tailscale-config">
<label class="toggle-row">
<input type="checkbox" id="tailscale-enable" />
<span>Enable Tailscale — access your services from anywhere</span>
</label>
<div class="tailscale-options hidden" id="tailscale-options">
<div class="config-grid">
<label>
<span>Tailscale Auth Key</span>
<input type="text" id="ts-authkey" placeholder="tskey-auth-..." />
</label>
<label>
<span>Hostname</span>
<input type="text" id="ts-hostname" value="stackpilot" placeholder="stackpilot" />
</label>
</div>
<label class="toggle-row sub">
<input type="checkbox" id="ts-funnel" />
<span>Enable Tailscale Funnel (expose selected services to the internet)</span>
</label>
<label class="toggle-row sub">
<input type="checkbox" id="ts-subnet" checked />
<span>Advertise as subnet router (access all services via Tailscale IP)</span>
</label>
<div class="ts-warning">
⚠️ Security note: Tailscale Funnel exposes services publicly. Subnet routing only shares with your Tailnet (your devices).
</div>
</div>
</div>
</section>
<!-- Step 4: Preview & Download -->
<section class="step" id="step-preview">
<h2>4. Preview & Download</h2>
<div class="preview-area">
<div class="preview-tabs">
<button class="tab active" data-tab="compose">docker-compose.yml</button>
<button class="tab" data-tab="env">.env</button>
<button class="tab" data-tab="setup">setup.sh</button>
</div>
<pre class="preview-code"><code id="preview-content">Select apps above to see preview...</code></pre>
</div>
<button class="download-btn" id="download-btn" disabled>📦 Download Stack Bundle (.zip)</button>
</section>
</main>
<footer class="footer">
<p>StackPilot — No server required. Everything runs in your browser.</p>
</footer>
<!-- Community Gallery -->
<section class="step gallery-section" id="step-gallery" style="display:none;">
<h2>🌐 Community Stacks</h2>
<div class="gallery-controls">
<input type="text" id="gallery-search" class="gallery-search" placeholder="Search community stacks..." />
<select id="gallery-sort" class="gallery-sort">
<option value="stars">⭐ Most Starred</option>
<option value="downloads">📦 Most Downloaded</option>
<option value="recent">🕐 Most Recent</option>
</select>
<button class="analyse-btn" id="share-stack-btn" style="margin:0;padding:0.5rem 1.2rem;font-size:0.85rem;">+ Share My Stack</button>
</div>
<div id="gallery-grid" class="gallery-grid"></div>
</section>
<!-- Auth Modal -->
<div class="modal-overlay hidden" id="auth-modal">
<div class="modal">
<button class="modal-close" id="modal-close">&times;</button>
<h2 id="auth-modal-title">Sign In</h2>
<form id="auth-form" class="auth-form">
<input type="text" id="auth-username" placeholder="Username" class="auth-input" style="display:none;" />
<input type="email" id="auth-email" placeholder="Email" class="auth-input" />
<input type="password" id="auth-password" placeholder="Password" class="auth-input" />
<button type="submit" class="analyse-btn" style="width:100%;margin:0;">Sign In</button>
<p class="auth-toggle">Don't have an account? <a href="#" id="auth-switch">Sign Up</a></p>
<p class="auth-error hidden" id="auth-error"></p>
</form>
</div>
</div>
<!-- Share Stack Modal -->
<div class="modal-overlay hidden" id="share-modal">
<div class="modal">
<button class="modal-close" id="share-modal-close">&times;</button>
<h2>Share Your Stack</h2>
<form id="share-form" class="auth-form">
<input type="text" id="share-name" placeholder="Stack name (e.g. My Media Setup)" class="auth-input" required />
<textarea id="share-desc" placeholder="Description (optional)" class="auth-input" style="min-height:80px;resize:vertical;"></textarea>
<input type="text" id="share-tags" placeholder="Tags (comma-separated, e.g. media, arr, beginner)" class="auth-input" />
<button type="submit" class="analyse-btn" style="width:100%;margin:0;">🚀 Share Stack</button>
</form>
</div>
</div>
<!-- Compose Analyser Modal/Section -->
<section class="step" id="step-analyser" style="display:none;">
<h2>🔍 Analyse Existing Docker Compose</h2>
<p class="analyser-desc">Paste or drop your docker-compose.yml below to get security, performance, and best-practice recommendations.</p>
<div class="drop-zone" id="drop-zone">
<textarea id="analyser-input" class="analyser-textarea" placeholder="Paste your docker-compose.yml here, or drag & drop a file..."></textarea>
<div class="drop-overlay" id="drop-overlay">📁 Drop docker-compose.yml here</div>
</div>
<button class="analyse-btn" id="analyse-btn">Analyse</button>
<div id="analyser-results" class="analyser-results"></div>
<button class="analyse-btn fixed-btn hidden" id="show-fixed-btn">✨ Show Fixed Compose</button>
<div class="view-toggle hidden" id="view-toggle">
<button class="view-btn active" data-view="fixed">Fixed</button>
<button class="view-btn" data-view="diff">Diff</button>
</div>
<div id="fixed-preview" class="fixed-preview hidden">
<h3>Improved docker-compose.yml</h3>
<pre class="preview-code"><code id="fixed-content"></code></pre>
<button class="download-btn" id="download-fixed-btn" style="margin-top:1rem;">📦 Download Fixed Compose</button>
</div>
<div id="diff-preview" class="fixed-preview hidden">
<h3>Changes (Before → After)</h3>
<pre class="preview-code diff-code"><code id="diff-content"></code></pre>
</div>
</section>
<script type="module" src="/src/main.js"></script>
</body>
</html>