Compare commits
2 Commits
96034abeea
...
54746f4636
| Author | SHA1 | Date | |
|---|---|---|---|
|
54746f4636
|
|||
|
6a30145171
|
@@ -1,3 +1,6 @@
|
||||
# Build output
|
||||
dist/
|
||||
|
||||
# Build artifacts
|
||||
*.tar
|
||||
*.tar.gz
|
||||
|
||||
+6
-8
@@ -1,16 +1,14 @@
|
||||
FROM docker.io/library/nginx:alpine
|
||||
|
||||
RUN rm /etc/nginx/conf.d/default.conf
|
||||
RUN rm /etc/nginx/conf.d/default.conf && \
|
||||
apk add --no-cache coreutils
|
||||
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
COPY src/index.html /usr/share/nginx/html/
|
||||
COPY src/style.css /usr/share/nginx/html/
|
||||
COPY src/script.js /usr/share/nginx/html/
|
||||
COPY src/profile.jpeg /usr/share/nginx/html/
|
||||
COPY src/ /src/
|
||||
COPY build.sh /build.sh
|
||||
RUN chmod +x /build.sh && SRC=/src DIST=/dist /bin/sh /build.sh && cp -r /dist/* /usr/share/nginx/html/ && rm -rf /src /build.sh /dist
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
|
||||
|
||||
CMD wget -qO- http://localhost:8080/ || exit 1
|
||||
HEALTHCHECK --interval=30s --timeout=3s --retries=3 CMD wget -qO- http://localhost:8080/ || exit 1
|
||||
@@ -13,7 +13,7 @@ podman build -t homepage:latest .
|
||||
### Run the container
|
||||
|
||||
```bash
|
||||
podman run --rm -p 8080:8080 -v ./src:/usr/share/nginx/html:z homepage:latest
|
||||
podman run --rm -p 0.0.0.0:8080:8080 -v ./src:/usr/share/nginx/html:z homepage:latest
|
||||
```
|
||||
|
||||
Visit `http://localhost:8080` in your browser.
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
SRC="${SRC:-src}"
|
||||
DIST="${DIST:-dist}"
|
||||
|
||||
# Clean and create dist
|
||||
rm -rf "$DIST"
|
||||
mkdir -p "$DIST"
|
||||
|
||||
# Copy index.html
|
||||
cp "$SRC/index.html" "$DIST/index.html"
|
||||
|
||||
# Cache-bust assets by appending a content hash to filenames
|
||||
for ext in css js jpeg png svg; do
|
||||
for file in "$SRC"/*."$ext"; do
|
||||
[ -f "$file" ] || continue
|
||||
name=$(basename "$file" | sed "s/\.$ext$//")
|
||||
hash=$(md5sum "$file" | cut -d' ' -f1)
|
||||
newname="${name}.${hash}.${ext}"
|
||||
|
||||
cp "$file" "$DIST/$newname"
|
||||
|
||||
# Update references in HTML
|
||||
case "$ext" in
|
||||
css)
|
||||
sed -i "s|href=\"${name}.${ext}\"|href=\"${newname}\"|g" "$DIST/index.html"
|
||||
;;
|
||||
js)
|
||||
sed -i "s|src=\"${name}.${ext}\"|src=\"${newname}\"|g" "$DIST/index.html"
|
||||
;;
|
||||
jpeg|png|svg)
|
||||
sed -i "s|\"${name}.${ext}\"|\"${newname}\"|g" "$DIST/index.html"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
done
|
||||
|
||||
echo "Build complete: $DIST/"
|
||||
echo "Assets with cache-busting hashes:"
|
||||
ls -1 "$DIST"
|
||||
+14
@@ -17,6 +17,20 @@ server {
|
||||
gzip_min_length 256;
|
||||
gzip_vary on;
|
||||
|
||||
# Never cache index.html so the latest HTML is always served
|
||||
location = /index.html {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
add_header Expires "0";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
# Cache hashed assets forever (filename contains content hash)
|
||||
location ~* \.(css|js|jpeg|jpg|png|gif|ico|svg|woff2?)$ {
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
+43
-43
@@ -3,9 +3,6 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||||
<meta http-equiv="Pragma" content="no-cache">
|
||||
<meta http-equiv="Expires" content="0">
|
||||
<title>Reese Wells - Self-Hosting & Infrastructure</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
@@ -15,10 +12,11 @@
|
||||
<div class="server-rack-bg"></div>
|
||||
|
||||
<!-- Navigation -->
|
||||
<nav>
|
||||
<nav aria-label="Main navigation">
|
||||
<div class="nav-inner">
|
||||
<div class="logo"><Reese /></div>
|
||||
<ul class="nav-links" id="navLinks">
|
||||
<ul class="nav-links" id="navLinks" role="list" aria-label="Primary navigation">
|
||||
<li><a href="#hero" aria-current="page">Home</a></li>
|
||||
<li><a href="#about">About</a></li>
|
||||
<li><a href="#experience">Experience</a></li>
|
||||
<li><a href="#skills">Skills</a></li>
|
||||
@@ -32,8 +30,9 @@
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main>
|
||||
<!-- Hero -->
|
||||
<section class="hero" id="hero">
|
||||
<section class="hero" id="hero" aria-label="Introduction">
|
||||
<div class="hero-content">
|
||||
<h1>
|
||||
Hi, I'm <span class="gradient-text">Reese Wells</span><br>
|
||||
@@ -55,7 +54,7 @@
|
||||
</section>
|
||||
|
||||
<!-- About -->
|
||||
<section id="about">
|
||||
<section id="about" aria-label="About">
|
||||
<div class="about-grid">
|
||||
<div class="about-image fade-in">
|
||||
<div class="avatar-frame">
|
||||
@@ -79,16 +78,16 @@
|
||||
services across a fleet of servers. Every service runs as a rootless container with
|
||||
dedicated systemd user sessions, backed by centralized BorgBackup.
|
||||
</p>
|
||||
<div class="about-stats">
|
||||
<div class="stat">
|
||||
<div class="about-stats" role="list" aria-label="Key statistics">
|
||||
<div class="stat" role="listitem">
|
||||
<div class="stat-number">80+</div>
|
||||
<div class="stat-label">Services</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat" role="listitem">
|
||||
<div class="stat-number">2</div>
|
||||
<div class="stat-label">Domains</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat" role="listitem">
|
||||
<div class="stat-number">100%</div>
|
||||
<div class="stat-label">Self-Hosted</div>
|
||||
</div>
|
||||
@@ -98,11 +97,11 @@
|
||||
</section>
|
||||
|
||||
<!-- Experience -->
|
||||
<section id="experience">
|
||||
<section id="experience" aria-label="Work Experience">
|
||||
<p class="section-label fade-in">Experience</p>
|
||||
<h2 class="section-title fade-in">Where I've worked</h2>
|
||||
<div class="timeline">
|
||||
<div class="timeline-item fade-in">
|
||||
<div class="timeline" role="list" aria-label="Work history">
|
||||
<div class="timeline-item fade-in" role="listitem">
|
||||
<div class="timeline-marker"></div>
|
||||
<div class="timeline-content">
|
||||
<div class="timeline-header">
|
||||
@@ -120,7 +119,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline-item fade-in">
|
||||
<div class="timeline-item fade-in" role="listitem">
|
||||
<div class="timeline-marker"></div>
|
||||
<div class="timeline-content">
|
||||
<div class="timeline-header">
|
||||
@@ -135,7 +134,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline-item fade-in">
|
||||
<div class="timeline-item fade-in" role="listitem">
|
||||
<div class="timeline-marker"></div>
|
||||
<div class="timeline-content">
|
||||
<div class="timeline-header">
|
||||
@@ -149,7 +148,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline-item fade-in">
|
||||
<div class="timeline-item fade-in" role="listitem">
|
||||
<div class="timeline-marker"></div>
|
||||
<div class="timeline-content">
|
||||
<div class="timeline-header">
|
||||
@@ -168,15 +167,15 @@
|
||||
</section>
|
||||
|
||||
<!-- Skills -->
|
||||
<div class="skills-section" id="skills">
|
||||
<div class="skills-section" id="skills" role="region" aria-label="Skills & Expertise">
|
||||
<div class="skills-inner">
|
||||
<p class="section-label fade-in">Skills & Expertise</p>
|
||||
<h2 class="section-title fade-in">What I work with</h2>
|
||||
<p class="section-desc fade-in">
|
||||
A broad toolkit focused on infrastructure, automation, and self-hosted services.
|
||||
</p>
|
||||
<div class="skills-grid">
|
||||
<div class="skill-card fade-in">
|
||||
<div class="skills-grid" role="list" aria-label="Skills">
|
||||
<div class="skill-card fade-in" role="listitem">
|
||||
<div class="skill-icon">⚙</div>
|
||||
<h3>Container Orchestration</h3>
|
||||
<p>Rootless containers managed via Podman quadlets, Docker Compose, and Kubernetes clusters with Helm charts.</p>
|
||||
@@ -187,7 +186,7 @@
|
||||
<span class="skill-tag">k3s</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="skill-card fade-in">
|
||||
<div class="skill-card fade-in" role="listitem">
|
||||
<div class="skill-icon">⚛</div>
|
||||
<h3>Infrastructure Automation</h3>
|
||||
<p>Ansible playbooks drive deployments across a multi-server fleet with strict SOP ordering and centralized configuration.</p>
|
||||
@@ -198,7 +197,7 @@
|
||||
<span class="skill-tag">BorgBackup</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="skill-card fade-in">
|
||||
<div class="skill-card fade-in" role="listitem">
|
||||
<div class="skill-icon">☁</div>
|
||||
<h3>Networking & DNS</h3>
|
||||
<p>AWS Route53 powers all DNS management with DDNS auto-updating, Caddy reverse proxy with Route53 DNS-validated TLS, and dual-domain strategy.</p>
|
||||
@@ -209,7 +208,7 @@
|
||||
<span class="skill-tag">WireGuard</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="skill-card fade-in">
|
||||
<div class="skill-card fade-in" role="listitem">
|
||||
<div class="skill-icon">🤖</div>
|
||||
<h3>Local AI & ML</h3>
|
||||
<p>Full local AI stack: Ollama, LiteLLM, LocalAI for inference, Langfuse for observability, with CUDA and ROCm support.</p>
|
||||
@@ -220,7 +219,7 @@
|
||||
<span class="skill-tag">ROCm</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="skill-card fade-in">
|
||||
<div class="skill-card fade-in" role="listitem">
|
||||
<div class="skill-icon">❐</div>
|
||||
<h3>Python</h3>
|
||||
<p>Python is the backbone of the homelab: DDNS updates, fleet-wide deployment scripts, AWS integration, and automation tooling with boto3, rich, and uv.</p>
|
||||
@@ -236,14 +235,14 @@
|
||||
</div>
|
||||
|
||||
<!-- Projects -->
|
||||
<section id="projects">
|
||||
<section id="projects" aria-label="Projects">
|
||||
<p class="section-label fade-in">Featured Projects</p>
|
||||
<h2 class="section-title fade-in">What I've built</h2>
|
||||
<p class="section-desc fade-in">
|
||||
A selection of projects from my homelab and deployment infrastructure.
|
||||
</p>
|
||||
<div class="projects-grid">
|
||||
<div class="project-card fade-in">
|
||||
<div class="projects-grid" role="list" aria-label="Projects">
|
||||
<div class="project-card fade-in" role="listitem">
|
||||
<div class="project-preview">
|
||||
<div class="code-block">
|
||||
<div class="code-line"><span class="code-keyword">version</span>: <span class="code-string">'3.8'</span></div>
|
||||
@@ -274,7 +273,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="project-card fade-in">
|
||||
<div class="project-card fade-in" role="listitem">
|
||||
<div class="project-preview">
|
||||
<div class="code-block">
|
||||
<div class="code-line"><span class="code-keyword">def</span> <span class="code-function">update_record</span>(<span class="code-variable">domain</span>):</div>
|
||||
@@ -305,7 +304,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="project-card fade-in">
|
||||
<div class="project-card fade-in" role="listitem">
|
||||
<div class="project-preview">
|
||||
<div class="code-block">
|
||||
<div class="code-line"><span class="code-keyword">from</span> <span class="code-string">ollama</span> <span class="code-keyword">import</span> <span class="code-function">Client</span></div>
|
||||
@@ -337,7 +336,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="project-card fade-in">
|
||||
<div class="project-card fade-in" role="listitem">
|
||||
<div class="project-preview">
|
||||
<div class="code-block">
|
||||
<div class="code-line"><span class="code-comment"># Ansible playbook</span></div>
|
||||
@@ -367,7 +366,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="project-card fade-in">
|
||||
<div class="project-card fade-in" role="listitem">
|
||||
<div class="project-preview">
|
||||
<div class="code-block">
|
||||
<div class="code-line"><span class="code-comment"># Self-hosted services</span></div>
|
||||
@@ -398,7 +397,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="project-card fade-in">
|
||||
<div class="project-card fade-in" role="listitem">
|
||||
<div class="project-preview">
|
||||
<div class="code-block">
|
||||
<div class="code-line"><span class="code-comment"># Kubernetes with k3s</span></div>
|
||||
@@ -432,24 +431,24 @@
|
||||
</section>
|
||||
|
||||
<!-- Contact -->
|
||||
<section class="contact-section" id="contact">
|
||||
<section class="contact-section" id="contact" aria-label="Contact">
|
||||
<p class="section-label fade-in">Get In Touch</p>
|
||||
<h2 class="section-title fade-in">Let's connect</h2>
|
||||
<p class="section-desc fade-in">
|
||||
Always open to discussing self-hosting, infrastructure, open source, or just sharing homelab stories.
|
||||
</p>
|
||||
<div class="contact-links fade-in">
|
||||
<a href="mailto:reese.wells@ducoterra.net" class="contact-link">
|
||||
<div class="contact-links fade-in" role="list" aria-label="Contact links">
|
||||
<a href="mailto:reese.wells@ducoterra.net" class="contact-link" role="listitem">
|
||||
<span class="link-icon">✉</span>
|
||||
<span>Email</span>
|
||||
</a>
|
||||
<a href="https://gitea.reeseapps.com/services/homelab" class="contact-link" target="_blank" rel="noopener">
|
||||
<a href="https://gitea.reeseapps.com/services/homelab" class="contact-link" target="_blank" rel="noopener" role="listitem">
|
||||
<span class="link-icon">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z"/></svg>
|
||||
</span>
|
||||
<span>Gitea</span>
|
||||
</a>
|
||||
<a href="https://www.linkedin.com/in/reesewells/" class="contact-link" target="_blank" rel="noopener">
|
||||
<a href="https://www.linkedin.com/in/reesewells/" class="contact-link" target="_blank" rel="noopener" role="listitem">
|
||||
<span class="link-icon">in</span>
|
||||
<span>LinkedIn</span>
|
||||
</a>
|
||||
@@ -457,14 +456,14 @@
|
||||
</section>
|
||||
|
||||
<!-- GPG Keys -->
|
||||
<section class="gpg-section" id="gpg">
|
||||
<section class="gpg-section" id="gpg" aria-label="Public GPG Keys">
|
||||
<p class="section-label fade-in">Trust</p>
|
||||
<h2 class="section-title fade-in">Public GPG Keys</h2>
|
||||
<p class="section-desc fade-in" style="margin-left:auto;margin-right:auto;text-align:center;">
|
||||
Use these keys to verify signed commits and communications. Both keys belong to Reese Wells.
|
||||
</p>
|
||||
<div class="gpg-keys-grid">
|
||||
<div class="gpg-key-card fade-in">
|
||||
<div class="gpg-keys-grid" role="list" aria-label="GPG keys">
|
||||
<div class="gpg-key-card fade-in" role="listitem">
|
||||
<div class="gpg-key-header">
|
||||
<span class="gpg-key-icon">🔒</span>
|
||||
<div>
|
||||
@@ -527,7 +526,7 @@ BQ==
|
||||
=U3eP
|
||||
-----END PGP PUBLIC KEY BLOCK-----</code></pre>
|
||||
</div>
|
||||
<div class="gpg-key-card fade-in">
|
||||
<div class="gpg-key-card fade-in" role="listitem">
|
||||
<div class="gpg-key-header">
|
||||
<span class="gpg-key-icon">🔒</span>
|
||||
<div>
|
||||
@@ -563,7 +562,7 @@ XwEAnes79w4eYeMUjIytQWACEvy4QoO7X2MLTKliSqc4Ag8=
|
||||
</section>
|
||||
|
||||
<!-- Achievements Tracker -->
|
||||
<section id="achievements" class="achievements-section" style="display: none;">
|
||||
<section id="achievements" class="achievements-section" hidden aria-label="Achievements">
|
||||
<div class="section-label">Achievements</div>
|
||||
<div class="section-title">Terminal Achievements</div>
|
||||
<p class="section-desc" style="margin: 0 auto 2rem;">Explore every corner of the terminal to unlock achievements.</p>
|
||||
@@ -572,7 +571,8 @@ XwEAnes79w4eYeMUjIytQWACEvy4QoO7X2MLTKliSqc4Ag8=
|
||||
</section>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer>
|
||||
</main>
|
||||
<footer role="contentinfo">
|
||||
<p style="font-size: 0.75rem; color: var(--text-muted);">
|
||||
Built with a 100% self-hosted LLM stack running
|
||||
<a href="https://github.com/ggml-org/llama.cpp" target="_blank" rel="noopener" style="color: var(--accent-hover); text-decoration: none;">llama.cpp</a>,
|
||||
|
||||
Reference in New Issue
Block a user