added more samples and identified ideal candidates
This commit is contained in:
@@ -0,0 +1,617 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>River Rooks | Terminal Index Blog Lander</title>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,700;1,400&family=Inter:wght@400;500;700&family=JetBrains+Mono:ital,wght@0,400;0,500;1,400&display=swap');
|
||||
|
||||
:root {
|
||||
--bg-canvas: #0d0f12; /* Deep slate/charcoal canvas */
|
||||
--bg-surface: #121417; /* Table & card surface */
|
||||
--bg-elevated: #1c1f26; /* Selected row / elevated state */
|
||||
--border-color: #2b303b; /* 1px solid low-contrast border */
|
||||
--text-primary: #e0e6f0; /* Light off-white prose */
|
||||
--text-muted: #8a95a5; /* Monospace metadata */
|
||||
--accent-cyan: #00f0ff; /* Primary terminal accent */
|
||||
--accent-green: #00e676; /* Operational indicator */
|
||||
--accent-amber: #ffb700; /* Warning / highlight */
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-canvas);
|
||||
color: var(--text-primary);
|
||||
font-family: 'IBM Plex Mono', monospace;
|
||||
line-height: 1.6;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 36px 16px 48px 16px;
|
||||
}
|
||||
|
||||
.app-wrapper {
|
||||
width: 100%;
|
||||
max-width: 960px;
|
||||
}
|
||||
|
||||
/* Terminal Window Frame */
|
||||
.terminal-window {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
background-color: var(--bg-surface);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Window Titlebar */
|
||||
.terminal-titlebar {
|
||||
background-color: var(--bg-canvas);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: 8px 14px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 0.78rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.window-controls {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.window-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: var(--border-color);
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.window-title {
|
||||
color: var(--text-primary);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* Terminal Body */
|
||||
.terminal-body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* System Header Info */
|
||||
.system-header {
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.system-identity {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.author-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.author-role {
|
||||
font-size: 0.8rem;
|
||||
color: var(--accent-cyan);
|
||||
}
|
||||
|
||||
.system-bio {
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-muted);
|
||||
max-width: 75ch;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Command Prompt & Live Grep Filter */
|
||||
.prompt-bar {
|
||||
background-color: var(--bg-canvas);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 3px;
|
||||
padding: 10px 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.prompt-text {
|
||||
color: var(--accent-cyan);
|
||||
font-weight: 700;
|
||||
font-size: 0.85rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.grep-label {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.grep-input {
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
color: var(--text-primary);
|
||||
font-family: 'IBM Plex Mono', monospace;
|
||||
font-size: 0.85rem;
|
||||
flex: 1;
|
||||
min-width: 140px;
|
||||
}
|
||||
|
||||
.grep-input::placeholder {
|
||||
color: var(--text-muted);
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.grep-count {
|
||||
color: var(--accent-green);
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
/* Tabular Directory View */
|
||||
.table-responsive {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.directory-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.82rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.directory-table th {
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
color: var(--text-muted);
|
||||
font-weight: 500;
|
||||
background-color: var(--bg-canvas);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.directory-table td {
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.directory-row {
|
||||
cursor: pointer;
|
||||
transition: none; /* Instant snap */
|
||||
}
|
||||
|
||||
.directory-row:hover {
|
||||
background-color: var(--bg-elevated);
|
||||
}
|
||||
|
||||
.directory-row.active {
|
||||
background-color: var(--bg-elevated);
|
||||
border-left: 2px solid var(--accent-cyan);
|
||||
}
|
||||
|
||||
.col-title {
|
||||
white-space: normal !important;
|
||||
min-width: 280px;
|
||||
}
|
||||
|
||||
.entry-link {
|
||||
color: var(--text-primary);
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
display: inline-block;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.directory-row:hover .entry-link,
|
||||
.directory-row.active .entry-link {
|
||||
color: var(--accent-cyan);
|
||||
}
|
||||
|
||||
.tag-badge {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.time-badge {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.preview-indicator {
|
||||
color: var(--accent-cyan);
|
||||
font-size: 0.75rem;
|
||||
text-decoration: none;
|
||||
padding: 2px 6px;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.directory-row:hover .preview-indicator {
|
||||
border-color: var(--accent-cyan);
|
||||
}
|
||||
|
||||
/* Live Inspection / Preview Pane */
|
||||
.preview-pane {
|
||||
background-color: var(--bg-canvas);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 3px;
|
||||
padding: 16px;
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.preview-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding-bottom: 8px;
|
||||
font-size: 0.78rem;
|
||||
color: var(--text-muted);
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.preview-filename {
|
||||
color: var(--accent-cyan);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.preview-abstract {
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-size: 0.92rem;
|
||||
color: var(--text-primary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.preview-code-block {
|
||||
background-color: var(--bg-surface);
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 12px;
|
||||
border-radius: 3px;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 0.78rem;
|
||||
overflow-x: auto;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.preview-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.read-btn {
|
||||
background-color: var(--accent-cyan);
|
||||
color: var(--bg-canvas);
|
||||
border: 1px solid var(--accent-cyan);
|
||||
padding: 4px 10px;
|
||||
font-family: 'IBM Plex Mono', monospace;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.read-btn:hover {
|
||||
background-color: transparent;
|
||||
color: var(--accent-cyan);
|
||||
}
|
||||
|
||||
/* Terminal Status Bar at Bottom */
|
||||
.terminal-statusbar {
|
||||
background-color: var(--bg-canvas);
|
||||
border-top: 1px solid var(--border-color);
|
||||
padding: 8px 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-muted);
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.footer-link {
|
||||
color: var(--text-primary);
|
||||
text-decoration: none;
|
||||
padding: 1px 4px;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.footer-link:hover {
|
||||
background-color: var(--accent-cyan);
|
||||
color: var(--bg-canvas);
|
||||
}
|
||||
|
||||
.footer-link:focus {
|
||||
outline: 2px solid var(--accent-cyan);
|
||||
}
|
||||
|
||||
.pipe {
|
||||
color: var(--border-color);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<main class="app-wrapper">
|
||||
|
||||
<div class="terminal-window">
|
||||
|
||||
<!-- Titlebar -->
|
||||
<div class="terminal-titlebar">
|
||||
<div class="window-controls">
|
||||
<span class="window-dot"></span>
|
||||
<span class="window-dot"></span>
|
||||
<span class="window-dot"></span>
|
||||
<span class="window-title" style="margin-left: 6px;">rooks@sys: ~/articles</span>
|
||||
</div>
|
||||
<div>POSIX SH • UTF-8 • [READ-ONLY]</div>
|
||||
</div>
|
||||
|
||||
<div class="terminal-body">
|
||||
|
||||
<!-- System Identity Header -->
|
||||
<header class="system-header">
|
||||
<div class="system-identity">
|
||||
<h1 class="author-title">River Rooks // Articles Directory</h1>
|
||||
<span class="author-role">[SYSTEMS ARCHITECTURE & CYBERSECURITY]</span>
|
||||
</div>
|
||||
<p class="system-bio">
|
||||
Field notes and technical write-ups covering embedded firmware reverse engineering, network forensics, low-level memory exploitation, and self-hosted infrastructure.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<!-- Command Prompt with Live Filter -->
|
||||
<div class="prompt-bar" role="search">
|
||||
<span class="prompt-text">dev-blog@system:~$</span>
|
||||
<span class="grep-label">grep -i</span>
|
||||
<input
|
||||
type="text"
|
||||
id="searchInput"
|
||||
class="grep-input"
|
||||
placeholder="filter by keyword, tag, or title..."
|
||||
oninput="handleSearch(this.value)"
|
||||
aria-label="Filter articles"
|
||||
>
|
||||
<span class="grep-count" id="matchCounter">5/5 matches</span>
|
||||
</div>
|
||||
|
||||
<!-- Directory Listing Table -->
|
||||
<div class="table-responsive">
|
||||
<table class="directory-table" id="directoryTable" aria-label="Articles directory table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">PERMS</th>
|
||||
<th scope="col">DATE</th>
|
||||
<th scope="col">PRIMARY TAG</th>
|
||||
<th scope="col">ARTICLE TITLE / FILE</th>
|
||||
<th scope="col">EST. TIME</th>
|
||||
<th scope="col">ACTION</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="tableBody">
|
||||
|
||||
<tr class="directory-row active" onclick="selectRow(0)" data-index="0" data-keywords="firmware hardware security bus-pirate spi flash extraction iot">
|
||||
<td><span style="color: var(--text-muted);">-rw-r--r--</span></td>
|
||||
<td><time datetime="2026-07-20" style="color: var(--accent-cyan);">2026-07-20</time></td>
|
||||
<td><span class="tag-badge">[#firmware]</span></td>
|
||||
<td class="col-title">
|
||||
<a href="#" class="entry-link">> Reverse Engineering IoT Firmware: SPI Flash Extraction</a>
|
||||
</td>
|
||||
<td><span class="time-badge">8 mins</span></td>
|
||||
<td><span class="preview-indicator">[INSPECT]</span></td>
|
||||
</tr>
|
||||
|
||||
<tr class="directory-row" onclick="selectRow(1)" data-index="1" data-keywords="homelab docker traefik networking proxy compose">
|
||||
<td><span style="color: var(--text-muted);">-rw-r--r--</span></td>
|
||||
<td><time datetime="2026-07-15" style="color: var(--accent-cyan);">2026-07-15</time></td>
|
||||
<td><span class="tag-badge">[#homelab]</span></td>
|
||||
<td class="col-title">
|
||||
<a href="#" class="entry-link">> Building a High-Availability Traefik Router with Docker Compose</a>
|
||||
</td>
|
||||
<td><span class="time-badge">12 mins</span></td>
|
||||
<td><span class="preview-indicator">[INSPECT]</span></td>
|
||||
</tr>
|
||||
|
||||
<tr class="directory-row" onclick="selectRow(2)" data-index="2" data-keywords="posix-sh shell unix portability scripts bashisms">
|
||||
<td><span style="color: var(--text-muted);">-rw-r--r--</span></td>
|
||||
<td><time datetime="2026-07-10" style="color: var(--accent-cyan);">2026-07-10</time></td>
|
||||
<td><span class="tag-badge">[#posix-sh]</span></td>
|
||||
<td class="col-title">
|
||||
<a href="#" class="entry-link">> POSIX sh vs. Bash: Portability in Modern Scripts</a>
|
||||
</td>
|
||||
<td><span class="time-badge">6 mins</span></td>
|
||||
<td><span class="preview-indicator">[INSPECT]</span></td>
|
||||
</tr>
|
||||
|
||||
<tr class="directory-row" onclick="selectRow(3)" data-index="3" data-keywords="ctf binary exploitation heap glibc 2.35 memory security">
|
||||
<td><span style="color: var(--text-muted);">-rw-r--r--</span></td>
|
||||
<td><time datetime="2026-06-28" style="color: var(--accent-cyan);">2026-06-28</time></td>
|
||||
<td><span class="tag-badge">[#ctf]</span></td>
|
||||
<td class="col-title">
|
||||
<a href="#" class="entry-link">> CTF Write-up: Heap Exploitation in glibc 2.35</a>
|
||||
</td>
|
||||
<td><span class="time-badge">15 mins</span></td>
|
||||
<td><span class="preview-indicator">[INSPECT]</span></td>
|
||||
</tr>
|
||||
|
||||
<tr class="directory-row" onclick="selectRow(4)" data-index="4" data-keywords="networking wireguard security zerotrust opnsense vlans">
|
||||
<td><span style="color: var(--text-muted);">-rw-r--r--</span></td>
|
||||
<td><time datetime="2026-06-14" style="color: var(--accent-cyan);">2026-06-14</time></td>
|
||||
<td><span class="tag-badge">[#networking]</span></td>
|
||||
<td class="col-title">
|
||||
<a href="#" class="entry-link">> Zero-Trust Network Isolation with WireGuard & OPNsense VLANs</a>
|
||||
</td>
|
||||
<td><span class="time-badge">10 mins</span></td>
|
||||
<td><span class="preview-indicator">[INSPECT]</span></td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Live Inspection / Details Panel -->
|
||||
<section class="preview-pane" id="previewPane" aria-label="Article Details Preview">
|
||||
<div class="preview-header">
|
||||
<span class="preview-filename" id="previewFilePath">~/notes/iot-firmware-spi-extraction.md</span>
|
||||
<span id="previewMeta">2026-07-20 • 8 mins read • [#firmware] [#hardware] [#security]</span>
|
||||
</div>
|
||||
<p class="preview-abstract" id="previewAbstract">
|
||||
In security audits of embedded systems, retrieving the firmware is the critical first step. When over-the-air updates are encrypted and debug ports (UART, JTAG) are disabled in production silicon, dumping physical SPI Flash memory becomes necessary. This writeup covers extracting firmware from an 8-pin SOP SPI flash chip using a Bus Pirate and flashrom to bypass bootloader lockdowns.
|
||||
</p>
|
||||
<div class="preview-code-block" id="previewCode">
|
||||
<pre><code># Query the SPI flash details using flashrom via Bus Pirate
|
||||
flashrom -p buspirate_spi:dev=/dev/ttyUSB0,spispeed=1M -r firmware_dump.bin
|
||||
|
||||
# Verify the integrity of the binary dump by comparing hashes
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Dump successful. Running md5sum..."
|
||||
md5sum firmware_dump.bin
|
||||
fi</code></pre>
|
||||
</div>
|
||||
<div class="preview-actions">
|
||||
<span style="font-size: 0.78rem; color: var(--text-muted);">Format: IEEE Std 1003.1 Markdown</span>
|
||||
<a href="#" class="read-btn" id="previewReadBtn">READ COMPLETE WRITEUP →</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Bottom Statusbar -->
|
||||
<footer class="terminal-statusbar">
|
||||
<div>5 FILES INDEXED • 51m READING DURATION • STATUS: 0 (OK)</div>
|
||||
<nav class="footer-links" aria-label="Terminal Footer Links">
|
||||
<a href="feed.xml" class="footer-link">feed.xml</a>
|
||||
<span class="pipe">|</span>
|
||||
<a href="https://git.rooks.dev" target="_blank" rel="noopener" class="footer-link">git.rooks.dev</a>
|
||||
<span class="pipe">|</span>
|
||||
<a href="https://github.com" target="_blank" rel="noopener" class="footer-link">github</a>
|
||||
<span class="pipe">|</span>
|
||||
<a href="index.html" class="footer-link">catalog ↗</a>
|
||||
</nav>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<script>
|
||||
const articlesData = [
|
||||
{
|
||||
path: '~/notes/iot-firmware-spi-extraction.md',
|
||||
date: '2026-07-20',
|
||||
time: '8 mins read',
|
||||
tags: '[#firmware] [#hardware] [#security] [#bus-pirate]',
|
||||
abstract: 'In security audits of embedded systems, retrieving the firmware is the critical first step. When over-the-air updates are encrypted and debug ports (UART, JTAG) are disabled in production silicon, dumping physical SPI Flash memory becomes necessary. This writeup covers extracting firmware from an 8-pin SOP SPI flash chip using a Bus Pirate and flashrom to bypass bootloader lockdowns.',
|
||||
code: `# Query the SPI flash details using flashrom via Bus Pirate\nflashrom -p buspirate_spi:dev=/dev/ttyUSB0,spispeed=1M -r firmware_dump.bin\n\n# Verify the integrity of the binary dump by comparing hashes\nif [ $? -eq 0 ]; then\n echo "Dump successful. Running md5sum..."\n md5sum firmware_dump.bin\nfi`
|
||||
},
|
||||
{
|
||||
path: '~/notes/ha-traefik-docker.md',
|
||||
date: '2026-07-15',
|
||||
time: '12 mins read',
|
||||
tags: '[#homelab] [#docker-traefik] [#networking]',
|
||||
abstract: 'Architecting automated TLS wildcard certificate propagation, Docker provider label routing, zero-downtime container updates, and backend health-check failover routes on bare-metal infrastructure.',
|
||||
code: `services:\n traefik:\n image: traefik:v3.1\n command:\n - "--providers.docker=true"\n - "--entrypoints.websecure.address=:443"\n - "--certificatesresolvers.cf.acme.dnschallenge=true"`
|
||||
},
|
||||
{
|
||||
path: '~/notes/posix-sh-vs-bash.md',
|
||||
date: '2026-07-10',
|
||||
time: '6 mins read',
|
||||
tags: '[#posix-sh] [#shell] [#unix]',
|
||||
abstract: 'Analyzing edge-case incompatibilities, syntax overhead, eliminating bashisms, and writing fast, bulletproof bootstrapping scripts for Alpine and embedded targets conforming to IEEE Std 1003.1.',
|
||||
code: `#!/bin/sh\n# Zero-allocation POSIX argument validation\n[ "$#" -ge 1 ] || { echo "Usage: $0 <target>" >&2; exit 1; }\ntarget="$1"\nprintf 'Configuring target node: %s\\n' "$target"`
|
||||
},
|
||||
{
|
||||
path: '~/notes/ctf-heap-glibc-2-35.md',
|
||||
date: '2026-06-28',
|
||||
time: '15 mins read',
|
||||
tags: '[#ctf] [#binary-exploitation] [#security]',
|
||||
abstract: 'Deconstructing modern glibc memory allocator defenses: tcache safe-linking pointer mangling, bypass primitives, and House of Apple file structure hijacking for arbitrary code execution.',
|
||||
code: `// glibc 2.35 tcache safe-linking pointer de-mangling\nuint64_t reveal_ptr(uint64_t mangled_ptr, uint64_t addr) {\n return mangled_ptr ^ (addr >> 12);\n}`
|
||||
},
|
||||
{
|
||||
path: '~/notes/zero-trust-wireguard-opnsense.md',
|
||||
date: '2026-06-14',
|
||||
time: '10 mins read',
|
||||
tags: '[#networking] [#wireguard] [#security]',
|
||||
abstract: 'Designing strict zone-based firewall rules across isolated VLANs (Management, Server DMZ, IoT, Trusted Clients) with point-to-point WireGuard cryptographic routing.',
|
||||
code: `[Interface]\nPrivateKey = aGlkZGVuX2tleV9kYXRhX2hlcmU=\nAddress = 10.100.0.5/32\nDNS = 10.100.0.1\n\n[Peer]\nPublicKey = cGVlcl9wdWJsaWNfa2V5X2RhdGFfaGVyZQ==\nEndpoint = vpn.rooks.dev:51820\nAllowedIPs = 10.100.0.0/24`
|
||||
}
|
||||
];
|
||||
|
||||
function selectRow(index) {
|
||||
document.querySelectorAll('.directory-row').forEach(row => row.classList.remove('active'));
|
||||
const activeRow = document.querySelector(`.directory-row[data-index="${index}"]`);
|
||||
if (activeRow) activeRow.classList.add('active');
|
||||
|
||||
const data = articlesData[index];
|
||||
if (!data) return;
|
||||
|
||||
document.getElementById('previewFilePath').innerText = data.path;
|
||||
document.getElementById('previewMeta').innerText = `${data.date} • ${data.time} • ${data.tags}`;
|
||||
document.getElementById('previewAbstract').innerText = data.abstract;
|
||||
document.getElementById('previewCode').innerHTML = `<pre><code>${escapeHtml(data.code)}</code></pre>`;
|
||||
}
|
||||
|
||||
function handleSearch(query) {
|
||||
const q = query.toLowerCase().trim();
|
||||
const rows = document.querySelectorAll('.directory-row');
|
||||
let visibleCount = 0;
|
||||
|
||||
rows.forEach(row => {
|
||||
const text = (row.innerText + ' ' + (row.getAttribute('data-keywords') || '')).toLowerCase();
|
||||
if (!q || text.includes(q)) {
|
||||
row.style.display = '';
|
||||
visibleCount++;
|
||||
} else {
|
||||
row.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('matchCounter').innerText = `${visibleCount}/5 matches`;
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
return str
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">");
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user