323 lines
18 KiB
HTML
323 lines
18 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Microservices Architecture</title>
|
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js" integrity="sha384-ZZ1pncU3bQe8y31yfZdMFdSpttDoPmOZg2wguVK9almUodir1PghgT0eY7Mrty8H" crossorigin="anonymous"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/jspdf@2.5.2/dist/jspdf.umd.min.js" integrity="sha384-en/ztfPSRkGfME4KIm05joYXynqzUgbsG5nMrj/xEFAHXkeZfO3yMK8QQ+mP7p1/" crossorigin="anonymous"></script>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body {
|
|
font-family: 'JetBrains Mono', monospace;
|
|
background: #020617;
|
|
min-height: 100vh;
|
|
padding: 2rem;
|
|
color: white;
|
|
}
|
|
.container { max-width: 1200px; margin: 0 auto; }
|
|
.header { margin-bottom: 2rem; }
|
|
.header-row { display: flex; align-items: center; gap: 1rem; margin-bottom: 0.5rem; }
|
|
.pulse-dot {
|
|
width: 12px; height: 12px; background: #34d399;
|
|
border-radius: 50%; animation: pulse 2s infinite;
|
|
}
|
|
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
|
|
h1 { font-size: 1.5rem; font-weight: 700; letter-spacing: -0.025em; }
|
|
.subtitle { color: #94a3b8; font-size: 0.875rem; margin-left: 1.75rem; }
|
|
.diagram-container {
|
|
background: rgba(15, 23, 42, 0.5);
|
|
border-radius: 1rem; border: 1px solid #1e293b;
|
|
padding: 1.5rem; overflow-x: auto;
|
|
}
|
|
svg { width: 100%; min-width: 900px; display: block; }
|
|
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1rem; margin-top: 2rem; }
|
|
.card { background: rgba(15, 23, 42, 0.5); border-radius: 0.75rem; border: 1px solid #1e293b; padding: 1.25rem; }
|
|
.card-header { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.75rem; }
|
|
.card-dot { width: 8px; height: 8px; border-radius: 50%; }
|
|
.card-dot.cyan { background: #22d3ee; }
|
|
.card-dot.emerald { background: #34d399; }
|
|
.card-dot.rose { background: #fb7185; }
|
|
.card h3 { font-size: 0.875rem; font-weight: 600; }
|
|
.card ul { list-style: none; color: #94a3b8; font-size: 0.75rem; }
|
|
.card li { margin-bottom: 0.375rem; }
|
|
.footer { text-align: center; margin-top: 1.5rem; color: #475569; font-size: 0.75rem; }
|
|
.toolbar { display: flex; gap: 0.5rem; margin-left: auto; flex-shrink: 0; align-items: center; }
|
|
.toolbar-toggle {
|
|
background: transparent; border: none; color: #475569; cursor: pointer;
|
|
font-size: 1.25rem; line-height: 1; padding: 0.25rem 0.5rem;
|
|
border-radius: 0.375rem; transition: color 0.2s, background 0.2s;
|
|
}
|
|
.toolbar-toggle:hover { color: #94a3b8; background: rgba(30, 41, 59, 0.5); }
|
|
.toolbar-actions { display: none; gap: 0.5rem; }
|
|
.toolbar.expanded .toolbar-actions { display: flex; }
|
|
.toolbar-actions button {
|
|
background: rgba(30, 41, 59, 0.8); border: 1px solid #334155; color: #94a3b8;
|
|
padding: 0.375rem 0.75rem; border-radius: 0.375rem; font-family: inherit;
|
|
font-size: 0.75rem; cursor: pointer; transition: all 0.2s; white-space: nowrap;
|
|
}
|
|
.toolbar-actions button:hover { background: rgba(51, 65, 85, 0.8); color: white; border-color: #475569; }
|
|
@media print {
|
|
body { background: #020617; padding: 1rem; }
|
|
.toolbar { display: none !important; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container" id="report-container">
|
|
<div class="header">
|
|
<div class="header-row">
|
|
<div class="pulse-dot"></div>
|
|
<h1>Microservices Architecture</h1>
|
|
<div class="toolbar">
|
|
<div class="toolbar-actions">
|
|
<button onclick="copyAsImage(this)">📋 Copy</button>
|
|
<button onclick="downloadPNG(this)">🖼️ PNG</button>
|
|
<button onclick="downloadPDF(this)">📄 PDF</button>
|
|
</div>
|
|
<button class="toolbar-toggle" onclick="this.parentElement.classList.toggle('expanded')" title="Export options" aria-label="Export options">⋯</button>
|
|
</div>
|
|
</div>
|
|
<p class="subtitle">Kubernetes-orchestrated services with API Gateway</p>
|
|
</div>
|
|
|
|
<div class="diagram-container">
|
|
<svg viewBox="0 0 950 560">
|
|
<defs>
|
|
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
|
<polygon points="0 0, 10 3.5, 0 7" fill="#64748b" />
|
|
</marker>
|
|
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
|
|
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="#1e293b" stroke-width="0.5"/>
|
|
</pattern>
|
|
</defs>
|
|
|
|
<rect width="100%" height="100%" fill="url(#grid)" />
|
|
|
|
<!-- Kubernetes Cluster boundary -->
|
|
<rect x="280" y="30" width="640" height="460" rx="12" fill="rgba(34, 211, 238, 0.03)" stroke="#22d3ee" stroke-width="1" stroke-dasharray="8,4"/>
|
|
<text x="295" y="52" fill="#22d3ee" font-size="10" font-weight="600">Kubernetes Cluster</text>
|
|
|
|
<!-- Arrows to Gateway -->
|
|
<line x1="120" y1="150" x2="188" y2="150" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
|
<line x1="120" y1="380" x2="188" y2="380" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
|
|
|
<!-- Gateway to services -->
|
|
<line x1="370" y1="180" x2="438" y2="100" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
|
<line x1="370" y1="200" x2="438" y2="200" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
|
<line x1="370" y1="220" x2="438" y2="300" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
|
<line x1="370" y1="360" x2="438" y2="400" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
|
|
|
<!-- Services to DBs -->
|
|
<line x1="560" y1="100" x2="698" y2="100" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
|
<line x1="560" y1="200" x2="698" y2="200" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
|
<line x1="560" y1="300" x2="698" y2="300" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
|
<line x1="560" y1="400" x2="698" y2="400" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
|
|
|
<!-- Message bus connections -->
|
|
<line x1="500" y1="130" x2="500" y2="138" stroke="#fb7185" stroke-width="1" stroke-dasharray="4,4"/>
|
|
<line x1="500" y1="162" x2="500" y2="170" stroke="#fb7185" stroke-width="1" stroke-dasharray="4,4"/>
|
|
<line x1="500" y1="230" x2="500" y2="238" stroke="#fb7185" stroke-width="1" stroke-dasharray="4,4"/>
|
|
<line x1="500" y1="262" x2="500" y2="270" stroke="#fb7185" stroke-width="1" stroke-dasharray="4,4"/>
|
|
<line x1="500" y1="330" x2="500" y2="338" stroke="#fb7185" stroke-width="1" stroke-dasharray="4,4"/>
|
|
<line x1="500" y1="362" x2="500" y2="370" stroke="#fb7185" stroke-width="1" stroke-dasharray="4,4"/>
|
|
|
|
<!-- Web Client -->
|
|
<rect x="20" y="110" width="100" height="80" rx="6" fill="#0f172a"/>
|
|
<rect x="20" y="110" width="100" height="80" rx="6" fill="rgba(8, 51, 68, 0.4)" stroke="#22d3ee" stroke-width="1.5"/>
|
|
<text x="70" y="145" fill="white" font-size="11" font-weight="600" text-anchor="middle">Web App</text>
|
|
<text x="70" y="165" fill="#94a3b8" font-size="9" text-anchor="middle">React SPA</text>
|
|
|
|
<!-- Mobile Client -->
|
|
<rect x="20" y="340" width="100" height="80" rx="6" fill="#0f172a"/>
|
|
<rect x="20" y="340" width="100" height="80" rx="6" fill="rgba(8, 51, 68, 0.4)" stroke="#22d3ee" stroke-width="1.5"/>
|
|
<text x="70" y="375" fill="white" font-size="11" font-weight="600" text-anchor="middle">Mobile App</text>
|
|
<text x="70" y="395" fill="#94a3b8" font-size="9" text-anchor="middle">iOS/Android</text>
|
|
|
|
<!-- API Gateway -->
|
|
<rect x="190" y="140" width="180" height="120" rx="6" fill="#0f172a"/>
|
|
<rect x="190" y="140" width="180" height="120" rx="6" fill="rgba(136, 19, 55, 0.4)" stroke="#fb7185" stroke-width="1.5"/>
|
|
<text x="280" y="175" fill="white" font-size="12" font-weight="600" text-anchor="middle">API Gateway</text>
|
|
<text x="280" y="195" fill="#94a3b8" font-size="9" text-anchor="middle">Kong / Nginx</text>
|
|
<text x="280" y="210" fill="#94a3b8" font-size="9" text-anchor="middle">Rate Limiting</text>
|
|
<text x="280" y="225" fill="#94a3b8" font-size="9" text-anchor="middle">Auth / Routing</text>
|
|
<text x="280" y="250" fill="#fb7185" font-size="8" text-anchor="middle">:443</text>
|
|
|
|
<!-- Auth Gateway for mobile -->
|
|
<rect x="190" y="340" width="180" height="80" rx="6" fill="#0f172a"/>
|
|
<rect x="190" y="340" width="180" height="80" rx="6" fill="rgba(136, 19, 55, 0.4)" stroke="#fb7185" stroke-width="1.5"/>
|
|
<text x="280" y="375" fill="white" font-size="11" font-weight="600" text-anchor="middle">Auth Service</text>
|
|
<text x="280" y="395" fill="#94a3b8" font-size="9" text-anchor="middle">OAuth 2.0 / JWT</text>
|
|
|
|
<!-- User Service -->
|
|
<rect x="440" y="70" width="120" height="60" rx="6" fill="#0f172a"/>
|
|
<rect x="440" y="70" width="120" height="60" rx="6" fill="rgba(6, 78, 59, 0.4)" stroke="#34d399" stroke-width="1.5"/>
|
|
<text x="500" y="95" fill="white" font-size="10" font-weight="600" text-anchor="middle">User Service</text>
|
|
<text x="500" y="115" fill="#94a3b8" font-size="8" text-anchor="middle">Go :8081</text>
|
|
|
|
<!-- Order Service -->
|
|
<rect x="440" y="170" width="120" height="60" rx="6" fill="#0f172a"/>
|
|
<rect x="440" y="170" width="120" height="60" rx="6" fill="rgba(6, 78, 59, 0.4)" stroke="#34d399" stroke-width="1.5"/>
|
|
<text x="500" y="195" fill="white" font-size="10" font-weight="600" text-anchor="middle">Order Service</text>
|
|
<text x="500" y="215" fill="#94a3b8" font-size="8" text-anchor="middle">Java :8082</text>
|
|
|
|
<!-- Product Service -->
|
|
<rect x="440" y="270" width="120" height="60" rx="6" fill="#0f172a"/>
|
|
<rect x="440" y="270" width="120" height="60" rx="6" fill="rgba(6, 78, 59, 0.4)" stroke="#34d399" stroke-width="1.5"/>
|
|
<text x="500" y="295" fill="white" font-size="10" font-weight="600" text-anchor="middle">Product Service</text>
|
|
<text x="500" y="315" fill="#94a3b8" font-size="8" text-anchor="middle">Python :8083</text>
|
|
|
|
<!-- Notification Service -->
|
|
<rect x="440" y="370" width="120" height="60" rx="6" fill="#0f172a"/>
|
|
<rect x="440" y="370" width="120" height="60" rx="6" fill="rgba(6, 78, 59, 0.4)" stroke="#34d399" stroke-width="1.5"/>
|
|
<text x="500" y="395" fill="white" font-size="10" font-weight="600" text-anchor="middle">Notification Svc</text>
|
|
<text x="500" y="415" fill="#94a3b8" font-size="8" text-anchor="middle">Node.js :8084</text>
|
|
|
|
<!-- Message Bus -->
|
|
<rect x="440" y="140" width="120" height="20" rx="4" fill="rgba(251, 146, 60, 0.3)" stroke="#fb923c" stroke-width="1"/>
|
|
<text x="500" y="154" fill="#fb923c" font-size="7" text-anchor="middle">Kafka / RabbitMQ</text>
|
|
|
|
<rect x="440" y="240" width="120" height="20" rx="4" fill="rgba(251, 146, 60, 0.3)" stroke="#fb923c" stroke-width="1"/>
|
|
<text x="500" y="254" fill="#fb923c" font-size="7" text-anchor="middle">Event Bus</text>
|
|
|
|
<rect x="440" y="340" width="120" height="20" rx="4" fill="rgba(251, 146, 60, 0.3)" stroke="#fb923c" stroke-width="1"/>
|
|
<text x="500" y="354" fill="#fb923c" font-size="7" text-anchor="middle">Event Bus</text>
|
|
|
|
<!-- PostgreSQL -->
|
|
<rect x="700" y="70" width="100" height="60" rx="6" fill="#0f172a"/>
|
|
<rect x="700" y="70" width="100" height="60" rx="6" fill="rgba(76, 29, 149, 0.4)" stroke="#a78bfa" stroke-width="1.5"/>
|
|
<text x="750" y="95" fill="white" font-size="10" font-weight="600" text-anchor="middle">PostgreSQL</text>
|
|
<text x="750" y="115" fill="#94a3b8" font-size="8" text-anchor="middle">Users DB</text>
|
|
|
|
<!-- MongoDB -->
|
|
<rect x="700" y="170" width="100" height="60" rx="6" fill="#0f172a"/>
|
|
<rect x="700" y="170" width="100" height="60" rx="6" fill="rgba(76, 29, 149, 0.4)" stroke="#a78bfa" stroke-width="1.5"/>
|
|
<text x="750" y="195" fill="white" font-size="10" font-weight="600" text-anchor="middle">MongoDB</text>
|
|
<text x="750" y="215" fill="#94a3b8" font-size="8" text-anchor="middle">Orders DB</text>
|
|
|
|
<!-- Elasticsearch -->
|
|
<rect x="700" y="270" width="100" height="60" rx="6" fill="#0f172a"/>
|
|
<rect x="700" y="270" width="100" height="60" rx="6" fill="rgba(76, 29, 149, 0.4)" stroke="#a78bfa" stroke-width="1.5"/>
|
|
<text x="750" y="295" fill="white" font-size="10" font-weight="600" text-anchor="middle">Elasticsearch</text>
|
|
<text x="750" y="315" fill="#94a3b8" font-size="8" text-anchor="middle">Products</text>
|
|
|
|
<!-- Redis -->
|
|
<rect x="700" y="370" width="100" height="60" rx="6" fill="#0f172a"/>
|
|
<rect x="700" y="370" width="100" height="60" rx="6" fill="rgba(76, 29, 149, 0.4)" stroke="#a78bfa" stroke-width="1.5"/>
|
|
<text x="750" y="395" fill="white" font-size="10" font-weight="600" text-anchor="middle">Redis</text>
|
|
<text x="750" y="415" fill="#94a3b8" font-size="8" text-anchor="middle">Cache / Queue</text>
|
|
|
|
<!-- Legend (placed below all boundaries) -->
|
|
<text x="300" y="510" fill="white" font-size="10" font-weight="600">Legend</text>
|
|
<rect x="300" y="522" width="16" height="10" rx="2" fill="rgba(6, 78, 59, 0.4)" stroke="#34d399" stroke-width="1"/>
|
|
<text x="322" y="530" fill="#94a3b8" font-size="8">Service</text>
|
|
<rect x="380" y="522" width="16" height="10" rx="2" fill="rgba(76, 29, 149, 0.4)" stroke="#a78bfa" stroke-width="1"/>
|
|
<text x="402" y="530" fill="#94a3b8" font-size="8">Database</text>
|
|
<rect x="470" y="522" width="16" height="10" rx="2" fill="rgba(136, 19, 55, 0.4)" stroke="#fb7185" stroke-width="1"/>
|
|
<text x="492" y="530" fill="#94a3b8" font-size="8">Gateway</text>
|
|
<rect x="560" y="522" width="16" height="10" rx="2" fill="rgba(251, 146, 60, 0.3)" stroke="#fb923c" stroke-width="1"/>
|
|
<text x="582" y="530" fill="#94a3b8" font-size="8">Message Bus</text>
|
|
</svg>
|
|
</div>
|
|
|
|
<div class="cards">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="card-dot cyan"></div>
|
|
<h3>Client Applications</h3>
|
|
</div>
|
|
<ul>
|
|
<li>• React SPA (Web)</li>
|
|
<li>• Native iOS/Android</li>
|
|
<li>• Unified API interface</li>
|
|
<li>• JWT authentication</li>
|
|
</ul>
|
|
</div>
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="card-dot emerald"></div>
|
|
<h3>Microservices</h3>
|
|
</div>
|
|
<ul>
|
|
<li>• Polyglot (Go, Java, Python, Node)</li>
|
|
<li>• Independent deployments</li>
|
|
<li>• Event-driven communication</li>
|
|
<li>• Database per service</li>
|
|
</ul>
|
|
</div>
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="card-dot rose"></div>
|
|
<h3>Infrastructure</h3>
|
|
</div>
|
|
<ul>
|
|
<li>• Kubernetes orchestration</li>
|
|
<li>• Kong API Gateway</li>
|
|
<li>• Kafka event streaming</li>
|
|
<li>• Prometheus monitoring</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<p class="footer">Microservices Architecture • Domain-driven design</p>
|
|
</div>
|
|
|
|
<script>
|
|
async function copyAsImage(btn) {
|
|
const orig = btn.textContent;
|
|
try {
|
|
const el = document.getElementById('report-container');
|
|
const r = el.getBoundingClientRect();
|
|
const pad = 32;
|
|
const canvas = await html2canvas(document.body, { backgroundColor: '#020617', scale: 2, useCORS: true, ignoreElements: (e) => e.classList && e.classList.contains('toolbar'), x: r.left + window.scrollX - pad, y: r.top + window.scrollY - pad, width: r.width + pad * 2, height: r.height + pad * 2 });
|
|
const blob = await new Promise(r => canvas.toBlob(r, 'image/png'));
|
|
await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })]);
|
|
btn.textContent = '✓ Copied!';
|
|
} catch (e) {
|
|
btn.textContent = '✗ Failed';
|
|
}
|
|
setTimeout(() => btn.textContent = orig, 2000);
|
|
}
|
|
async function downloadPNG(btn) {
|
|
const orig = btn.textContent;
|
|
btn.textContent = '⏳ ...';
|
|
try {
|
|
const el = document.getElementById('report-container');
|
|
const r = el.getBoundingClientRect();
|
|
const pad = 32;
|
|
const canvas = await html2canvas(document.body, { backgroundColor: '#020617', scale: 2, useCORS: true, ignoreElements: (e) => e.classList && e.classList.contains('toolbar'), x: r.left + window.scrollX - pad, y: r.top + window.scrollY - pad, width: r.width + pad * 2, height: r.height + pad * 2 });
|
|
const link = document.createElement('a');
|
|
link.download = 'microservices.png';
|
|
link.href = canvas.toDataURL('image/png');
|
|
link.click();
|
|
btn.textContent = '✓ Done!';
|
|
} catch (e) {
|
|
btn.textContent = '✗ Failed';
|
|
}
|
|
setTimeout(() => btn.textContent = orig, 2000);
|
|
}
|
|
async function downloadPDF(btn) {
|
|
const orig = btn.textContent;
|
|
btn.textContent = '⏳ ...';
|
|
try {
|
|
const el = document.getElementById('report-container');
|
|
const r = el.getBoundingClientRect();
|
|
const pad = 32;
|
|
const canvas = await html2canvas(document.body, { backgroundColor: '#020617', scale: 2, useCORS: true, ignoreElements: (e) => e.classList && e.classList.contains('toolbar'), x: r.left + window.scrollX - pad, y: r.top + window.scrollY - pad, width: r.width + pad * 2, height: r.height + pad * 2 });
|
|
const imgData = canvas.toDataURL('image/png');
|
|
const { jsPDF } = window.jspdf;
|
|
const orientation = canvas.width > canvas.height ? 'landscape' : 'portrait';
|
|
const pdf = new jsPDF({ orientation, unit: 'px', format: [canvas.width, canvas.height], hotfixes: ['px_scaling'] });
|
|
pdf.addImage(imgData, 'PNG', 0, 0, canvas.width, canvas.height);
|
|
pdf.save('microservices.pdf');
|
|
btn.textContent = '✓ Done!';
|
|
} catch (e) {
|
|
btn.textContent = '✗ Failed';
|
|
}
|
|
setTimeout(() => btn.textContent = orig, 2000);
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|