272 lines
14 KiB
HTML
272 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>AWS Serverless 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: #fbbf24;
|
|
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.amber { background: #fbbf24; }
|
|
.card-dot.emerald { background: #34d399; }
|
|
.card-dot.violet { background: #a78bfa; }
|
|
.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>AWS Serverless 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">Lambda + API Gateway + DynamoDB</p>
|
|
</div>
|
|
|
|
<div class="diagram-container">
|
|
<svg viewBox="0 0 950 480">
|
|
<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)" />
|
|
|
|
<!-- AWS Region -->
|
|
<rect x="140" y="30" width="780" height="420" rx="12" fill="rgba(251, 191, 36, 0.05)" stroke="#fbbf24" stroke-width="1" stroke-dasharray="8,4"/>
|
|
<text x="155" y="52" fill="#fbbf24" font-size="10" font-weight="600">AWS Region: us-east-1</text>
|
|
|
|
<!-- Arrows -->
|
|
<line x1="120" y1="200" x2="188" y2="200" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
|
<line x1="310" y1="200" x2="378" y2="200" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
|
<line x1="500" y1="200" x2="568" y2="200" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
|
<line x1="690" y1="200" x2="758" y2="200" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
|
|
|
<!-- Lambda to S3 -->
|
|
<line x1="630" y1="240" x2="630" y2="318" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
|
|
|
<!-- Lambda to SQS -->
|
|
<line x1="630" y1="160" x2="630" y2="102" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrowhead)"/>
|
|
|
|
<!-- Users -->
|
|
<rect x="20" y="160" width="100" height="80" rx="6" fill="#0f172a"/>
|
|
<rect x="20" y="160" width="100" height="80" rx="6" fill="rgba(30, 41, 59, 0.5)" stroke="#94a3b8" stroke-width="1.5"/>
|
|
<text x="70" y="195" fill="white" font-size="12" font-weight="600" text-anchor="middle">Clients</text>
|
|
<text x="70" y="215" fill="#94a3b8" font-size="9" text-anchor="middle">Web/Mobile</text>
|
|
|
|
<!-- CloudFront -->
|
|
<rect x="190" y="160" width="120" height="80" rx="6" fill="#0f172a"/>
|
|
<rect x="190" y="160" width="120" height="80" rx="6" fill="rgba(120, 53, 15, 0.3)" stroke="#fbbf24" stroke-width="1.5"/>
|
|
<text x="250" y="195" fill="white" font-size="11" font-weight="600" text-anchor="middle">CloudFront</text>
|
|
<text x="250" y="215" fill="#94a3b8" font-size="9" text-anchor="middle">CDN</text>
|
|
|
|
<!-- API Gateway -->
|
|
<rect x="380" y="150" width="120" height="100" rx="6" fill="#0f172a"/>
|
|
<rect x="380" y="150" width="120" height="100" rx="6" fill="rgba(120, 53, 15, 0.3)" stroke="#fbbf24" stroke-width="1.5"/>
|
|
<text x="440" y="185" fill="white" font-size="11" font-weight="600" text-anchor="middle">API Gateway</text>
|
|
<text x="440" y="205" fill="#94a3b8" font-size="9" text-anchor="middle">REST API</text>
|
|
<text x="440" y="220" fill="#94a3b8" font-size="9" text-anchor="middle">WebSocket</text>
|
|
<text x="440" y="240" fill="#fbbf24" font-size="8" text-anchor="middle">HTTPS</text>
|
|
|
|
<!-- Lambda -->
|
|
<rect x="570" y="140" width="120" height="120" rx="6" fill="#0f172a"/>
|
|
<rect x="570" y="140" width="120" height="120" rx="6" fill="rgba(6, 78, 59, 0.4)" stroke="#34d399" stroke-width="1.5"/>
|
|
<text x="630" y="180" fill="white" font-size="11" font-weight="600" text-anchor="middle">Lambda</text>
|
|
<text x="630" y="200" fill="#94a3b8" font-size="9" text-anchor="middle">Functions</text>
|
|
<text x="630" y="215" fill="#94a3b8" font-size="9" text-anchor="middle">Node.js/Python</text>
|
|
<text x="630" y="248" fill="#34d399" font-size="8" text-anchor="middle">Auto-scaling</text>
|
|
|
|
<!-- DynamoDB -->
|
|
<rect x="760" y="150" width="130" height="100" rx="6" fill="#0f172a"/>
|
|
<rect x="760" y="150" width="130" height="100" rx="6" fill="rgba(76, 29, 149, 0.4)" stroke="#a78bfa" stroke-width="1.5"/>
|
|
<text x="825" y="185" fill="white" font-size="11" font-weight="600" text-anchor="middle">DynamoDB</text>
|
|
<text x="825" y="205" fill="#94a3b8" font-size="9" text-anchor="middle">NoSQL Database</text>
|
|
<text x="825" y="220" fill="#94a3b8" font-size="9" text-anchor="middle">On-demand</text>
|
|
<text x="825" y="240" fill="#a78bfa" font-size="8" text-anchor="middle">Single-digit ms</text>
|
|
|
|
<!-- S3 -->
|
|
<rect x="570" y="320" width="120" height="90" rx="6" fill="#0f172a"/>
|
|
<rect x="570" y="320" width="120" height="90" rx="6" fill="rgba(120, 53, 15, 0.3)" stroke="#fbbf24" stroke-width="1.5"/>
|
|
<text x="630" y="355" fill="white" font-size="11" font-weight="600" text-anchor="middle">S3</text>
|
|
<text x="630" y="375" fill="#94a3b8" font-size="9" text-anchor="middle">Object Storage</text>
|
|
<text x="630" y="400" fill="#fbbf24" font-size="8" text-anchor="middle">Static Assets</text>
|
|
|
|
<!-- SQS -->
|
|
<rect x="570" y="60" width="120" height="40" rx="6" fill="#0f172a"/>
|
|
<rect x="570" y="60" width="120" height="40" rx="6" fill="rgba(120, 53, 15, 0.3)" stroke="#fbbf24" stroke-width="1.5"/>
|
|
<text x="630" y="85" fill="white" font-size="11" font-weight="600" text-anchor="middle">SQS</text>
|
|
|
|
<!-- Legend -->
|
|
<text x="170" y="420" fill="white" font-size="10" font-weight="600">Legend</text>
|
|
<rect x="170" y="432" width="16" height="10" rx="2" fill="rgba(120, 53, 15, 0.3)" stroke="#fbbf24" stroke-width="1"/>
|
|
<text x="192" y="440" fill="#94a3b8" font-size="8">AWS Service</text>
|
|
<rect x="270" y="432" width="16" height="10" rx="2" fill="rgba(6, 78, 59, 0.4)" stroke="#34d399" stroke-width="1"/>
|
|
<text x="292" y="440" fill="#94a3b8" font-size="8">Compute</text>
|
|
<rect x="360" y="432" width="16" height="10" rx="2" fill="rgba(76, 29, 149, 0.4)" stroke="#a78bfa" stroke-width="1"/>
|
|
<text x="382" y="440" fill="#94a3b8" font-size="8">Database</text>
|
|
</svg>
|
|
</div>
|
|
|
|
<div class="cards">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="card-dot amber"></div>
|
|
<h3>Infrastructure</h3>
|
|
</div>
|
|
<ul>
|
|
<li>• CloudFront CDN distribution</li>
|
|
<li>• API Gateway REST endpoints</li>
|
|
<li>• S3 static asset hosting</li>
|
|
<li>• SQS message queues</li>
|
|
</ul>
|
|
</div>
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="card-dot emerald"></div>
|
|
<h3>Compute</h3>
|
|
</div>
|
|
<ul>
|
|
<li>• Lambda functions (Node.js)</li>
|
|
<li>• Auto-scaling to zero</li>
|
|
<li>• Pay-per-invocation</li>
|
|
<li>• 15 min max execution</li>
|
|
</ul>
|
|
</div>
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="card-dot violet"></div>
|
|
<h3>Data</h3>
|
|
</div>
|
|
<ul>
|
|
<li>• DynamoDB tables</li>
|
|
<li>• On-demand capacity</li>
|
|
<li>• Global secondary indexes</li>
|
|
<li>• Point-in-time recovery</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<p class="footer">AWS Serverless Architecture • Event-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 = 'aws-serverless.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('aws-serverless.pdf');
|
|
btn.textContent = '✓ Done!';
|
|
} catch (e) {
|
|
btn.textContent = '✗ Failed';
|
|
}
|
|
setTimeout(() => btn.textContent = orig, 2000);
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|