feat: add tools section with MarkItDown, update Caddyfile for md subdomain
This commit is contained in:
@@ -74,27 +74,6 @@ portainer.carloselugo.com {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Umami Analytics ───────────────────────────────────────────────────────────
|
|
||||||
analytics.carloselugo.com {
|
|
||||||
import security_headers
|
|
||||||
encode gzip
|
|
||||||
log {
|
|
||||||
output file /var/log/caddy/analytics.log
|
|
||||||
format json
|
|
||||||
}
|
|
||||||
|
|
||||||
@public path /script.js /api/send
|
|
||||||
handle @public {
|
|
||||||
reverse_proxy umami:3000
|
|
||||||
}
|
|
||||||
|
|
||||||
handle {
|
|
||||||
@blocked not remote_ip 24.139.235.46/32 192.168.68.0/24 2605:ba00:3108::/48 74.115.203.2/32 165.225.192.0/18
|
|
||||||
abort @blocked
|
|
||||||
reverse_proxy umami:3000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── ntfy (notificaciones push) ────────────────────────────────────────────────
|
# ── ntfy (notificaciones push) ────────────────────────────────────────────────
|
||||||
ntfy.carloselugo.com {
|
ntfy.carloselugo.com {
|
||||||
reverse_proxy ntfy:80 {
|
reverse_proxy ntfy:80 {
|
||||||
@@ -126,3 +105,38 @@ logs.carloselugo.com {
|
|||||||
format json
|
format json
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
# ── NI Quiz — evento público ───────────────────────────────────────────────────
|
||||||
|
ni-quiz.carloselugo.com {
|
||||||
|
import security_headers
|
||||||
|
reverse_proxy ni-quiz:8000 {
|
||||||
|
header_up Host {host}
|
||||||
|
header_up X-Real-IP {remote_host}
|
||||||
|
}
|
||||||
|
log {
|
||||||
|
output file /var/log/caddy/ni-quiz.log
|
||||||
|
format json
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# ── MarkItDown ────────────────────────────────────────────────────────────────
|
||||||
|
md.carloselugo.com {
|
||||||
|
import security_headers
|
||||||
|
reverse_proxy markitdown:8000
|
||||||
|
log {
|
||||||
|
output file /var/log/caddy/markitdown.log
|
||||||
|
format json
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── NI Quiz — evento público ───────────────────────────────────────────────────
|
||||||
|
route-rush.carloselugo.com {
|
||||||
|
import security_headers
|
||||||
|
reverse_proxy route-rush:8001 {
|
||||||
|
header_up Host {host}
|
||||||
|
header_up X-Real-IP {remote_host}
|
||||||
|
}
|
||||||
|
log {
|
||||||
|
output file /var/log/caddy/ni-quiz.log
|
||||||
|
format json
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
const tools = [
|
||||||
|
{
|
||||||
|
id: '01',
|
||||||
|
title: 'MarkItDown',
|
||||||
|
desc: 'Convert any document to Markdown. Drop a PDF, DOCX, PPTX, XLSX, HTML, or CSV and get clean Markdown output instantly.',
|
||||||
|
tags: ['python', 'fastapi', 'markitdown'],
|
||||||
|
url: 'https://md.carloselugo.com',
|
||||||
|
status: 'live',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const statusLabel = {
|
||||||
|
live: { text: 'live', color: '#c8ff00' },
|
||||||
|
wip: { text: 'wip', color: '#ff6b35' },
|
||||||
|
}
|
||||||
|
|
||||||
|
export function renderTools() {
|
||||||
|
const cards = tools.map(t => {
|
||||||
|
const s = statusLabel[t.status]
|
||||||
|
return `
|
||||||
|
<a class="tool-card reveal" href="${t.url}" target="_blank" rel="noopener">
|
||||||
|
<div class="card-top">
|
||||||
|
<span class="card-id mono">${t.id}</span>
|
||||||
|
<span class="card-status mono" style="color:${s.color}">● ${s.text}</span>
|
||||||
|
</div>
|
||||||
|
<h3 class="card-title">${t.title}</h3>
|
||||||
|
<p class="card-desc">${t.desc}</p>
|
||||||
|
<div class="tool-bottom">
|
||||||
|
<div class="card-tags">
|
||||||
|
${t.tags.map(tag => `<span class="tag">${tag}</span>`).join('')}
|
||||||
|
</div>
|
||||||
|
<span class="tool-link mono">open ↗</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
`
|
||||||
|
}).join('')
|
||||||
|
|
||||||
|
return `
|
||||||
|
<section class="tools" id="tools">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-head reveal">
|
||||||
|
<div class="accent-line"></div>
|
||||||
|
<h2 class="section-title">Tools</h2>
|
||||||
|
<p class="section-sub">Self-hosted utilities running on the homelab.</p>
|
||||||
|
</div>
|
||||||
|
<div class="tool-grid">
|
||||||
|
${cards}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.tools { padding: 0 0 100px; }
|
||||||
|
.section-sub {
|
||||||
|
font-size: 0.88rem;
|
||||||
|
color: var(--muted);
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
.tool-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||||
|
gap: 1px;
|
||||||
|
background: var(--border);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
margin-top: 48px;
|
||||||
|
}
|
||||||
|
.tool-card {
|
||||||
|
background: var(--bg-card);
|
||||||
|
padding: 32px;
|
||||||
|
transition: background 0.2s;
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
.tool-card:hover { background: #161616; }
|
||||||
|
.tool-card:hover .tool-link { color: var(--accent); letter-spacing: 0.12em; }
|
||||||
|
.tool-bottom {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 20px;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.tool-link {
|
||||||
|
font-size: 0.72rem;
|
||||||
|
color: var(--muted);
|
||||||
|
white-space: nowrap;
|
||||||
|
transition: color 0.2s, letter-spacing 0.2s;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
`
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import './style.css'
|
|||||||
import { renderHeader } from './components/header.js'
|
import { renderHeader } from './components/header.js'
|
||||||
import { renderHero } from './components/hero.js'
|
import { renderHero } from './components/hero.js'
|
||||||
import { renderProjects } from './components/projects.js'
|
import { renderProjects } from './components/projects.js'
|
||||||
|
import { renderTools } from './components/tools.js'
|
||||||
import { renderBlog, initBlog } from './components/blog.js'
|
import { renderBlog, initBlog } from './components/blog.js'
|
||||||
import { renderFooter } from './components/footer.js'
|
import { renderFooter } from './components/footer.js'
|
||||||
|
|
||||||
@@ -12,6 +13,7 @@ app.innerHTML = `
|
|||||||
<main>
|
<main>
|
||||||
${renderHero()}
|
${renderHero()}
|
||||||
${renderProjects()}
|
${renderProjects()}
|
||||||
|
${renderTools()}
|
||||||
${renderBlog()}
|
${renderBlog()}
|
||||||
</main>
|
</main>
|
||||||
${renderFooter()}
|
${renderFooter()}
|
||||||
|
|||||||
Reference in New Issue
Block a user