feat: rebuild xiteng.site homelab platform

This commit is contained in:
2026-08-12 09:48:25 +08:00
parent 557b0eca33
commit 5b84988789
128 changed files with 14979 additions and 292 deletions
+522
View File
@@ -0,0 +1,522 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>xiteng.site — Personal Infrastructure</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
<style>
:root {
--bg: #050508;
--surface: #0a0a14;
--card: rgba(255,255,255,0.025);
--card-hover: rgba(255,255,255,0.05);
--text: #e4e4ec;
--text-dim: #8888a0;
--text-muted: #5a5a72;
--accent: #6366f1;
--accent-glow: rgba(99,102,241,0.35);
--green: #22c55e;
--green-glow: rgba(34,197,94,0.3);
--amber: #f59e0b;
--blue: #3b82f6;
--red: #ef4444;
--border: rgba(255,255,255,0.06);
--border-visible: rgba(255,255,255,0.10);
--grid: rgba(255,255,255,0.025);
}
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth;background:var(--bg);-webkit-text-size-adjust:100%}
body{
font-family:'Inter',system-ui,-apple-system,sans-serif;
background:var(--bg);color:var(--text);
overflow-x:hidden;line-height:1.6;
-webkit-font-smoothing:antialiased;
}
/* ── Canvas Background ── */
#gridCanvas{position:fixed;inset:0;z-index:0;pointer-events:none;opacity:0.6}
/* ── Status Bar ── */
.statusbar{
position:fixed;top:0;left:0;right:0;z-index:100;
height:36px;display:flex;align-items:center;justify-content:space-between;
padding:0 20px;font-family:'JetBrains Mono',monospace;
font-size:11px;color:var(--text-muted);
background:rgba(5,5,8,0.85);
backdrop-filter:blur(12px);
border-bottom:1px solid var(--border);
}
.statusbar a{display:inline-flex;align-items:center;color:var(--text-dim);text-decoration:none;font-weight:600}
.statusbar a:hover{color:var(--text)}
.statusbar .left{display:flex;align-items:center;gap:16px}
.statusbar .dot{
width:7px;height:7px;border-radius:50%;background:var(--green);
box-shadow:0 0 6px var(--green-glow);
margin-right:8px;display:inline-block;vertical-align:middle;
animation:pulse-dot 2s ease-in-out infinite;
}
@keyframes pulse-dot{0%,100%{opacity:1}50%{opacity:0.5}}
.statusbar .right{display:flex;gap:16px}
/* ── Layout ── */
.container{position:relative;z-index:1;max-width:1100px;margin:0 auto;padding:0 24px}
section{padding:100px 0;position:relative}
section:first-of-type{padding-top:160px}
/* ── Typography ── */
.label{
font-family:'JetBrains Mono',monospace;
font-size:12px;font-weight:500;color:var(--accent);
text-transform:uppercase;letter-spacing:0.08em;margin-bottom:16px;display:block;
}
h1{
font-size:clamp(40px,6vw,80px);font-weight:700;
line-height:1.05;letter-spacing:-0.03em;margin-bottom:16px;
}
h1 .hl{color:var(--accent)}
h2{
font-size:clamp(28px,3.5vw,48px);font-weight:600;
line-height:1.15;letter-spacing:-0.02em;margin-bottom:12px;
}
.subtitle{
font-size:clamp(15px,1.4vw,20px);font-weight:300;color:var(--text-dim);
max-width:560px;line-height:1.7;
}
/* ── Hero ── */
.hero-grid{
display:grid;grid-template-columns:repeat(3,1fr);gap:12px;
margin-top:48px;max-width:600px;
}
.hero-stat{
background:var(--card);border:1px solid var(--border);
border-radius:8px;padding:16px 18px;
}
.hero-stat .val{
font-family:'JetBrains Mono',monospace;
font-size:22px;font-weight:700;color:var(--text);
}
.hero-stat .val.up{color:var(--green)}
.hero-stat .lbl{font-size:11px;color:var(--text-muted);margin-top:2px;text-transform:uppercase;letter-spacing:0.05em}
.hero-stat .val .unit{font-size:13px;color:var(--text-dim);font-weight:400}
/* ── Terminal Block ── */
.terminal{
background:rgba(0,0,0,0.5);border:1px solid var(--border-visible);
border-radius:10px;overflow:hidden;font-family:'JetBrains Mono',monospace;
font-size:clamp(11px,0.8vw,13px);line-height:1.8;
}
.terminal .bar{
height:28px;background:rgba(255,255,255,0.03);
display:flex;align-items:center;padding:0 12px;gap:6px;
border-bottom:1px solid var(--border);
}
.terminal .bar .tb{width:10px;height:10px;border-radius:50%}
.terminal .bar .tb.r{background:var(--red)}
.terminal .bar .tb.y{background:var(--amber)}
.terminal .bar .tb.g{background:var(--green)}
.terminal .bar .ttl{font-size:11px;color:var(--text-muted);margin-left:8px}
.terminal .body{padding:16px 18px;color:var(--text-dim)}
.terminal .body .prompt{color:var(--green)}
.terminal .body .cmd{color:var(--text)}
.terminal .body .out{color:var(--text-dim)}
.terminal .body .cursor{
display:inline-block;width:8px;height:15px;background:var(--accent);
vertical-align:text-bottom;margin-left:1px;
animation:blink 1s step-end infinite;
}
@keyframes blink{50%{opacity:0}}
/* ── Architecture Diagram ── */
.arch-diagram{
display:flex;align-items:center;gap:0;flex-wrap:wrap;
justify-content:center;padding:20px 0;
}
.arch-node{
background:var(--surface);border:1px solid var(--border-visible);
border-radius:12px;padding:20px 28px;text-align:center;
transition:all 0.3s;position:relative;
}
.arch-node:hover{
border-color:var(--accent);
box-shadow:0 0 24px var(--accent-glow);
transform:translateY(-2px);
}
.arch-node .n{font-size:18px;font-weight:600;color:var(--text)}
.arch-node .d{font-size:11px;color:var(--text-muted);margin-top:3px;font-family:'JetBrains Mono',monospace}
.arch-node.accent{border-color:var(--accent);background:rgba(99,102,241,0.04)}
.arch-node.accent .n{color:var(--accent)}
.arch-arrow{color:var(--text-muted);font-size:18px;padding:0 10px;font-family:'JetBrains Mono',monospace;flex-shrink:0}
.arch-pulse{animation:arch-glow 2.5s ease-in-out infinite}
.arch-pulse:nth-child(2){animation-delay:0.5s}
.arch-pulse:nth-child(3){animation-delay:1s}
@keyframes arch-glow{0%,100%{box-shadow:0 0 0 rgba(99,102,241,0)}50%{box-shadow:0 0 20px var(--accent-glow)}}
/* ── Service Cards ── */
.card-grid{
display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));
gap:16px;margin-top:40px;
}
.svc-card{
background:var(--card);border:1px solid var(--border);
border-radius:12px;padding:28px;cursor:pointer;
transition:all 0.3s;position:relative;overflow:hidden;
}
.svc-card::before{
content:'';position:absolute;inset:0;border-radius:12px;
background:radial-gradient(600px circle at var(--mx,50%) var(--my,50%),rgba(99,102,241,0.06),transparent 40%);
opacity:0;transition:opacity 0.3s;
}
.svc-card:hover::before{opacity:1}
.svc-card:hover{
border-color:var(--border-visible);
background:var(--card-hover);
transform:translateY(-2px);
}
.svc-card .indicator{
width:8px;height:8px;border-radius:50%;background:var(--green);
box-shadow:0 0 8px var(--green-glow);
display:inline-block;margin-bottom:16px;
animation:pulse-dot 3s ease-in-out infinite;
}
.svc-card .indicator:nth-child(odd){animation-delay:1.5s}
.svc-card h3{font-size:20px;font-weight:600;margin-bottom:4px;letter-spacing:-0.01em}
.svc-card .url{
font-family:'JetBrains Mono',monospace;font-size:11px;
color:var(--text-muted);margin-bottom:12px;
}
.svc-card p{font-size:13px;color:var(--text-dim);line-height:1.6}
.svc-card .tags{margin-top:14px;display:flex;gap:6px;flex-wrap:wrap}
.svc-card .tag{
font-size:10px;font-weight:600;text-transform:uppercase;
letter-spacing:0.04em;padding:3px 8px;border-radius:4px;
color:var(--accent);background:rgba(99,102,241,0.08);
border:1px solid rgba(99,102,241,0.12);
}
/* ── Stack Pills ── */
.stack-section{display:flex;flex-direction:column;gap:20px;margin-top:40px}
.stack-row{display:flex;align-items:flex-start;gap:16px;flex-wrap:wrap}
.stack-label{
font-family:'JetBrains Mono',monospace;font-size:12px;font-weight:700;
color:var(--green);min-width:80px;text-align:right;padding-top:5px;
}
.stack-pills{display:flex;gap:8px;flex-wrap:wrap}
.stack-pill{
padding:6px 14px;border-radius:20px;font-size:13px;font-weight:500;
background:var(--card);border:1px solid var(--border);
color:var(--text-dim);transition:all 0.2s;
}
.stack-pill:hover{border-color:var(--border-visible);color:var(--text)}
.stack-pill.hl{border-color:var(--accent);color:var(--accent);background:rgba(99,102,241,0.06)}
/* ── CTA / Command Prompt ── */
.cmd-prompt{
background:var(--surface);border:1px solid var(--border-visible);
border-radius:12px;padding:20px 24px;display:flex;align-items:center;
gap:12px;font-family:'JetBrains Mono',monospace;font-size:15px;
max-width:560px;margin:0 auto;margin-top:48px;transition:all 0.3s;
}
.cmd-prompt:focus-within{border-color:var(--accent);box-shadow:0 0 20px var(--accent-glow)}
.cmd-prompt .pr{color:var(--green);flex-shrink:0}
.cmd-prompt input{
flex:1;background:none;border:none;outline:none;
color:var(--text);font:inherit;caret-color:var(--accent);
}
.cmd-prompt input::placeholder{color:var(--text-muted)}
/* ── Footer ── */
footer{
text-align:center;padding:60px 24px;border-top:1px solid var(--border);
font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--text-muted);
}
footer a{color:var(--accent);text-decoration:none}
footer a:hover{text-decoration:underline}
/* ── Scroll Reveal ── */
.reveal{opacity:0;transform:translateY(30px);transition:all 0.7s cubic-bezier(0.16,1,0.3,1)}
.reveal.visible{opacity:1;transform:translateY(0)}
/* ── Responsive ── */
@media(max-width:768px){
section{padding:60px 0}
section:first-of-type{padding-top:120px}
.arch-diagram{flex-direction:column;gap:4px}
.arch-arrow{display:none}
.hero-grid{grid-template-columns:repeat(2,1fr)}
.stack-row{flex-direction:column;gap:6px}
.stack-label{text-align:left;min-width:auto}
.card-grid{grid-template-columns:1fr}
.statusbar .right{display:none}
}
@media(max-width:400px){
.hero-grid{grid-template-columns:1fr 1fr;gap:6px}
.hero-stat{padding:10px 12px}
.hero-stat .val{font-size:18px}
}
</style>
</head>
<body>
<canvas id="gridCanvas"></canvas>
<!-- ═══ Status Bar ═══ -->
<header class="statusbar">
<div class="left"><a href="https://xiteng.site/?focus=deck#services">← xiteng.site</a><span><span class="dot"></span> all systems operational</span></div>
<div class="right">UTC+8 · Arch Linux · Docker · <span style="color:var(--green)">UP 42d</span></div>
</header>
<main class="container">
<!-- ═══ Hero ═══ -->
<section>
<div class="reveal">
<span class="label">// Personal Infrastructure</span>
<h1><span class="hl">xiteng</span>.site</h1>
<p class="subtitle">
自建开发基础设施。一台 Linux 机器,Docker Compose 驱动,
Cloudflare Tunnel 接入,Authentik 统一认证。
</p>
</div>
<div class="reveal hero-grid">
<div class="hero-stat"><div class="val up">8</div><div class="lbl">Services</div></div>
<div class="hero-stat"><div class="val">1<span class="unit"> host</span></div><div class="lbl">Arch Linux</div></div>
<div class="hero-stat"><div class="val up">99.9<span class="unit">%</span></div><div class="lbl">Uptime (30d)</div></div>
</div>
<div class="reveal" style="margin-top:40px">
<div class="terminal">
<div class="bar"><div class="tb r"></div><div class="tb y"></div><div class="tb g"></div><span class="ttl">ssh aliyun — bash — 80×24</span></div>
<div class="body">
<span class="prompt">$</span> <span class="cmd">docker compose ps</span><br>
<span class="out">NAME &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;STATUS<br>
gitea &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Up 7 days<br>
hedgedoc &nbsp;&nbsp;&nbsp;&nbsp;Up 7 days<br>
seaweedfs &nbsp;&nbsp;&nbsp;Up 7 days<br>
uptime-kuma &nbsp;Up 7 days<br>
homepage &nbsp;&nbsp;&nbsp;&nbsp;Up 7 days<br>
authentik &nbsp;&nbsp;&nbsp;Up 7 days<br>
deck &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Up 5 minutes<br>
cloudflared &nbsp;Up 7 days</span><br>
<span class="prompt">$</span> <span class="cursor"></span>
</div>
</div>
</div>
</section>
<!-- ═══ Architecture ═══ -->
<section>
<div class="reveal"><span class="label">// How It Works</span></div>
<div class="reveal"><h2>Traffic Flow</h2></div>
<div class="reveal arch-diagram">
<div class="arch-node"><div class="n">Internet</div><div class="d">HTTPS</div></div>
<div class="arch-arrow"></div>
<div class="arch-node accent arch-pulse"><div class="n">Cloudflare</div><div class="d">Tunnel · CDN</div></div>
<div class="arch-arrow"></div>
<div class="arch-node arch-pulse"><div class="n">Traefik</div><div class="d">TLS · Router</div></div>
<div class="arch-arrow"></div>
<div class="arch-node accent arch-pulse"><div class="n">Authentik</div><div class="d">SSO · Auth</div></div>
<div class="arch-arrow"></div>
<div class="arch-node"><div class="n">Services</div><div class="d">8 containers</div></div>
</div>
</section>
<!-- ═══ Services ═══ -->
<section>
<div class="reveal"><span class="label">// Services</span></div>
<div class="reveal"><h2>What's Running</h2></div>
<div class="reveal card-grid">
<a href="https://git.xiteng.site" class="svc-card" style="text-decoration:none;color:inherit">
<div class="indicator"></div>
<h3>Gitea</h3>
<div class="url">git.xiteng.site</div>
<p>自建 Git 托管。Pull Request、CI/CD Actions、包仓库、OAuth2 登录。</p>
<div class="tags"><span class="tag">git</span><span class="tag">ci/cd</span><span class="tag">oauth2</span></div>
</a>
<a href="https://notes.xiteng.site" class="svc-card" style="text-decoration:none;color:inherit">
<div class="indicator"></div>
<h3>HedgeDoc</h3>
<div class="url">notes.xiteng.site</div>
<p>实时协作 Markdown。Mermaid 图表、MathJax 公式、OIDC 登录。</p>
<div class="tags"><span class="tag">docs</span><span class="tag">collab</span><span class="tag">oidc</span></div>
</a>
<a href="https://s3.xiteng.site" class="svc-card" style="text-decoration:none;color:inherit">
<div class="indicator"></div>
<h3>SeaweedFS</h3>
<div class="url">s3.xiteng.site</div>
<p>S3 兼容对象存储。60MB 镜像,替代 MinIO。Filer UI + REST API。</p>
<div class="tags"><span class="tag">s3</span><span class="tag">storage</span></div>
</a>
<a href="https://status.xiteng.site" class="svc-card" style="text-decoration:none;color:inherit">
<div class="indicator"></div>
<h3>Uptime Kuma</h3>
<div class="url">status.xiteng.site</div>
<p>健康监控。60s 间隔探测,飞书/邮件告警,公开状态页。</p>
<div class="tags"><span class="tag">monitor</span><span class="tag">alerts</span></div>
</a>
<a href="https://xiteng.site" class="svc-card" style="text-decoration:none;color:inherit">
<div class="indicator"></div>
<h3>Homepage</h3>
<div class="url">xiteng.site</div>
<p>统一仪表盘。Docker 自动发现,书签 & 搜索,OIDC 登录。</p>
<div class="tags"><span class="tag">dashboard</span><span class="tag">oidc</span></div>
</a>
<a href="https://auth.xiteng.site" class="svc-card" style="text-decoration:none;color:inherit">
<div class="indicator"></div>
<h3>Authentik</h3>
<div class="url">auth.xiteng.site</div>
<p>身份认证平台。OAuth2/OIDC ProviderForwardAuth 代理保护。</p>
<div class="tags"><span class="tag">sso</span><span class="tag">security</span></div>
</a>
</div>
</section>
<!-- ═══ Stack ═══ -->
<section>
<div class="reveal"><span class="label">// Stack</span></div>
<div class="reveal"><h2>Technology</h2></div>
<div class="reveal stack-section">
<div class="stack-row">
<div class="stack-label">Infra</div>
<div class="stack-pills">
<span class="stack-pill hl">Docker Compose</span>
<span class="stack-pill">Arch Linux</span>
<span class="stack-pill">Cloudflare Tunnel</span>
<span class="stack-pill">frp Transit</span>
</div>
</div>
<div class="stack-row">
<div class="stack-label">Proxy</div>
<div class="stack-pills">
<span class="stack-pill hl">Traefik v3</span>
<span class="stack-pill">Let's Encrypt</span>
<span class="stack-pill">Cloudflare DNS</span>
</div>
</div>
<div class="stack-row">
<div class="stack-label">Data</div>
<div class="stack-pills">
<span class="stack-pill hl">PostgreSQL</span>
<span class="stack-pill">Redis</span>
<span class="stack-pill">SeaweedFS</span>
</div>
</div>
<div class="stack-row">
<div class="stack-label">Connect</div>
<div class="stack-pills">
<span class="stack-pill hl">Authentik</span>
<span class="stack-pill">Hermes Agent</span>
<span class="stack-pill">Feishu Bot</span>
</div>
</div>
</div>
</section>
<!-- ═══ CTA ═══ -->
<section style="text-align:center">
<div class="reveal">
<span class="label">// Try It</span>
<h2>Open a Terminal</h2>
<p class="subtitle" style="margin:0 auto 0 auto">访问任意服务,Authentik 统一认证。或输入命令探索。</p>
</div>
<div class="reveal cmd-prompt">
<span class="pr">$</span>
<input type="text" id="cmdInput" placeholder="输入 help 查看可用命令…" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">
</div>
</section>
</main>
<footer>
<a href="https://git.xiteng.site">git.xiteng.site</a> &nbsp;·&nbsp;
<a href="https://notes.xiteng.site">notes</a> &nbsp;·&nbsp;
<a href="https://status.xiteng.site">status</a> &nbsp;·&nbsp;
<a href="https://xiteng.site">home</a> &nbsp;·&nbsp;
Built with Hermes Agent
</footer>
<script>
// ═══ Grid Canvas ═══
(function(){
const c=document.getElementById('gridCanvas'),x=c.getContext('2d');
let W,H,mx=-100,my=-100;
function resize(){W=c.width=window.innerWidth;H=c.height=window.innerHeight}
resize();window.addEventListener('resize',resize);
document.addEventListener('mousemove',e=>{mx=e.clientX;my=e.clientY});
const dots=[];
for(let i=0;i<80;i++)dots.push({x:Math.random()*2000,y:Math.random()*2000,r:1+Math.random()*1.5,vx:(Math.random()-0.5)*0.3,vy:(Math.random()-0.5)*0.3});
function draw(){
x.clearRect(0,0,W,H);
// Grid
x.strokeStyle='rgba(255,255,255,0.018)';x.lineWidth=0.5;
const s=50;
for(let gx=s;gx<W;gx+=s){x.beginPath();x.moveTo(gx,0);x.lineTo(gx,H);x.stroke()}
for(let gy=s;gy<H;gy+=s){x.beginPath();x.moveTo(0,gy);x.lineTo(W,gy);x.stroke()}
// Dots
dots.forEach(d=>{
d.x+=d.vx;d.y+=d.vy;
if(d.x<0)d.x=2000;if(d.x>2000)d.x=0;
if(d.y<0)d.y=2000;if(d.y>2000)d.y=0;
const dx=d.x-mx,dy=d.y-my,dist=Math.sqrt(dx*dx+dy*dy);
const glow=Math.max(0,1-dist/200);
const r=d.r+glow*2;
x.beginPath();x.arc(d.x,d.y,r,0,Math.PI*2);
const a=0.15+glow*0.4;
x.fillStyle=`rgba(99,102,241,${a})`;x.fill();
});
requestAnimationFrame(draw);
}
draw();
})();
// ═══ Scroll Reveal ═══
(function(){
const obs=new IntersectionObserver(entries=>{
entries.forEach(e=>{if(e.isIntersecting)e.target.classList.add('visible')});
},{threshold:0.15});
document.querySelectorAll('.reveal').forEach(el=>obs.observe(el));
})();
// ═══ Card Glow Follow ═══
document.querySelectorAll('.svc-card').forEach(card=>{
card.addEventListener('mousemove',e=>{
const r=card.getBoundingClientRect();
card.style.setProperty('--mx',(e.clientX-r.left)+'px');
card.style.setProperty('--my',(e.clientY-r.top)+'px');
});
});
// ═══ Command Prompt ═══
(function(){
const input=document.getElementById('cmdInput');
const routes={
'git':'https://git.xiteng.site',
'notes':'https://notes.xiteng.site',
's3':'https://s3.xiteng.site',
'status':'https://status.xiteng.site',
'home':'https://xiteng.site',
'auth':'https://auth.xiteng.site',
'deck':'https://deck.xiteng.site',
'file':'https://file.xiteng.site',
};
input.addEventListener('keydown',e=>{
if(e.key!=='Enter')return;
const v=input.value.trim().toLowerCase();
input.value='';
if(v==='help'||v==='?'){
let msg='Available: ';
Object.keys(routes).forEach(k=>msg+=k+' ');
msg+='\nType a service name to navigate, or "help" to see this.';
alert(msg);
}else if(routes[v]){
window.open(routes[v],'_blank');
}else if(v){
alert(`Unknown command: ${v}\nType "help" to see available commands.`);
}
});
})();
</script>
</body>
</html>