mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-26 05:34:38 +02:00
feat(infra): add Cloudflare fallback plan + self-hosted landing pages
Two infrastructure improvements for tech independence:
1. Cloudflare Fallback Documentation (docs/CLOUDFLARE_FALLBACK.md):
- Plan B: WireGuard + Caddy on Hetzner VPS (€3.79/mo)
- Complete Caddyfile with all 30+ subdomains
- Step-by-step failover checklist (~15 min to switch)
- Plan C: Direct IP with ISP
2. Self-Hosted Landing Pages (eliminates Cloudflare Pages dependency):
- Nginx container (mana-infra-landings) on port 4400
- Multi-site config: each subdomain → separate dist/ folder
- Build script: scripts/mac-mini/build-landings.sh
- Cloudflare Tunnel ingress rules for 10 landing page domains
- Storage: /Volumes/ManaData/landings/ on external SSD
- Domains: it, chats, pics, zitares, presis, clocks,
manadeck, nutriphi, citycorners, docs
Migration path: Build landings locally, set Cloudflare DNS to
tunnel instead of Pages, then decommission CF Pages projects.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
954b204bac
commit
e3115b302d
11 changed files with 733 additions and 27 deletions
|
|
@ -212,6 +212,22 @@ const manascoreCollection = defineCollection({
|
|||
seo: z.number().min(0).max(100),
|
||||
})
|
||||
.optional(),
|
||||
// Dependency health metrics
|
||||
dependencies: z
|
||||
.object({
|
||||
total: z.number(), // Total dependency count
|
||||
outdated: z.number(), // Packages with available updates
|
||||
vulnerabilities: z
|
||||
.object({
|
||||
critical: z.number().default(0),
|
||||
high: z.number().default(0),
|
||||
moderate: z.number().default(0),
|
||||
low: z.number().default(0),
|
||||
})
|
||||
.optional(),
|
||||
lastChecked: z.string().optional(), // ISO date
|
||||
})
|
||||
.optional(),
|
||||
// Score history for trend visualization
|
||||
history: z
|
||||
.array(
|
||||
|
|
|
|||
|
|
@ -18,6 +18,15 @@ scores:
|
|||
documentation: 98
|
||||
security: 92
|
||||
ux: 95
|
||||
dependencies:
|
||||
total: 42
|
||||
outdated: 9
|
||||
vulnerabilities:
|
||||
critical: 0
|
||||
high: 0
|
||||
moderate: 0
|
||||
low: 0
|
||||
lastChecked: '2026-03-24'
|
||||
lighthouse:
|
||||
performance: 92
|
||||
accessibility: 95
|
||||
|
|
|
|||
|
|
@ -18,6 +18,15 @@ scores:
|
|||
documentation: 95
|
||||
security: 90
|
||||
ux: 94
|
||||
dependencies:
|
||||
total: 38
|
||||
outdated: 9
|
||||
vulnerabilities:
|
||||
critical: 0
|
||||
high: 0
|
||||
moderate: 0
|
||||
low: 0
|
||||
lastChecked: '2026-03-24'
|
||||
lighthouse:
|
||||
performance: 90
|
||||
accessibility: 93
|
||||
|
|
|
|||
|
|
@ -274,6 +274,113 @@ function getBarColor(score: number): string {
|
|||
})()
|
||||
}
|
||||
|
||||
{/* Dependency Health */}
|
||||
{
|
||||
audit.data.dependencies &&
|
||||
(() => {
|
||||
const deps = audit.data.dependencies;
|
||||
const vulnCount = deps.vulnerabilities
|
||||
? deps.vulnerabilities.critical +
|
||||
deps.vulnerabilities.high +
|
||||
deps.vulnerabilities.moderate +
|
||||
deps.vulnerabilities.low
|
||||
: 0;
|
||||
const healthPct = Math.round(((deps.total - deps.outdated) / deps.total) * 100);
|
||||
|
||||
return (
|
||||
<div class="border-border/50 mb-8 rounded-xl border bg-gradient-to-br from-white/5 to-white/[0.02] p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<h2 class="text-foreground text-sm font-semibold">Dependency Health</h2>
|
||||
<p class="text-muted-foreground text-xs">
|
||||
Paketstand und Sicherheit
|
||||
{deps.lastChecked ? ` (geprüft ${deps.lastChecked})` : ''}
|
||||
</p>
|
||||
</div>
|
||||
<span
|
||||
class={`text-lg font-bold ${healthPct >= 80 ? 'text-emerald-500' : healthPct >= 60 ? 'text-yellow-500' : 'text-red-500'}`}
|
||||
>
|
||||
{healthPct}% aktuell
|
||||
</span>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4 sm:grid-cols-4">
|
||||
<div>
|
||||
<span class="text-foreground text-2xl font-bold">{deps.total}</span>
|
||||
<span class="text-muted-foreground block text-xs">Pakete gesamt</span>
|
||||
</div>
|
||||
<div>
|
||||
<span
|
||||
class={`text-2xl font-bold ${deps.outdated > 10 ? 'text-yellow-500' : 'text-foreground'}`}
|
||||
>
|
||||
{deps.outdated}
|
||||
</span>
|
||||
<span class="text-muted-foreground block text-xs">Veraltet</span>
|
||||
</div>
|
||||
<div>
|
||||
<span
|
||||
class={`text-2xl font-bold ${vulnCount > 0 ? 'text-red-500' : 'text-emerald-500'}`}
|
||||
>
|
||||
{vulnCount}
|
||||
</span>
|
||||
<span class="text-muted-foreground block text-xs">Vulnerabilities</span>
|
||||
</div>
|
||||
<div>
|
||||
{deps.vulnerabilities ? (
|
||||
<div class="flex gap-1.5 mt-1">
|
||||
{deps.vulnerabilities.critical > 0 && (
|
||||
<span class="bg-red-500/10 text-red-500 rounded px-1.5 py-0.5 text-[10px] font-medium">
|
||||
{deps.vulnerabilities.critical} Critical
|
||||
</span>
|
||||
)}
|
||||
{deps.vulnerabilities.high > 0 && (
|
||||
<span class="bg-orange-500/10 text-orange-500 rounded px-1.5 py-0.5 text-[10px] font-medium">
|
||||
{deps.vulnerabilities.high} High
|
||||
</span>
|
||||
)}
|
||||
{deps.vulnerabilities.moderate > 0 && (
|
||||
<span class="bg-yellow-500/10 text-yellow-500 rounded px-1.5 py-0.5 text-[10px] font-medium">
|
||||
{deps.vulnerabilities.moderate} Mod
|
||||
</span>
|
||||
)}
|
||||
{deps.vulnerabilities.low > 0 && (
|
||||
<span class="bg-blue-500/10 text-blue-500 rounded px-1.5 py-0.5 text-[10px] font-medium">
|
||||
{deps.vulnerabilities.low} Low
|
||||
</span>
|
||||
)}
|
||||
{vulnCount === 0 && (
|
||||
<span class="bg-emerald-500/10 text-emerald-500 rounded px-1.5 py-0.5 text-[10px] font-medium">
|
||||
✓ Sicher
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<span class="text-muted-foreground text-xs">Keine Daten</span>
|
||||
)}
|
||||
<span class="text-muted-foreground block text-xs mt-1">Schweregrad</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* Health bar */}
|
||||
<div class="mt-4">
|
||||
<div class="bg-muted h-2 overflow-hidden rounded-full">
|
||||
<div
|
||||
class={`h-full rounded-full transition-all ${healthPct >= 80 ? 'bg-emerald-500' : healthPct >= 60 ? 'bg-yellow-500' : 'bg-red-500'}`}
|
||||
style={`width: ${healthPct}%`}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex justify-between mt-1">
|
||||
<span class="text-muted-foreground/50 text-[10px]">
|
||||
{deps.outdated} veraltet
|
||||
</span>
|
||||
<span class="text-muted-foreground/50 text-[10px]">
|
||||
{deps.total - deps.outdated} aktuell
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})()
|
||||
}
|
||||
|
||||
{/* Stats */}
|
||||
{
|
||||
audit.data.stats && (
|
||||
|
|
|
|||
|
|
@ -172,6 +172,46 @@ const statuses = [...new Set(sortedAudits.map((a) => a.data.status))];
|
|||
))}
|
||||
</div>
|
||||
|
||||
{/* Dependency health */}
|
||||
{data.dependencies &&
|
||||
(() => {
|
||||
const deps = data.dependencies;
|
||||
const vulnCount = deps.vulnerabilities
|
||||
? deps.vulnerabilities.critical +
|
||||
deps.vulnerabilities.high +
|
||||
deps.vulnerabilities.moderate +
|
||||
deps.vulnerabilities.low
|
||||
: 0;
|
||||
const healthPct = Math.round(
|
||||
((deps.total - deps.outdated) / deps.total) * 100
|
||||
);
|
||||
const healthColor =
|
||||
vulnCount > 0
|
||||
? deps.vulnerabilities?.critical || deps.vulnerabilities?.high
|
||||
? 'text-red-500'
|
||||
: 'text-yellow-500'
|
||||
: healthPct >= 80
|
||||
? 'text-emerald-500'
|
||||
: 'text-yellow-500';
|
||||
return (
|
||||
<div class="mt-2 flex items-center gap-3">
|
||||
<span class="text-muted-foreground/50 text-[9px]">Deps:</span>
|
||||
<span class={`text-[10px] font-medium ${healthColor}`}>
|
||||
{deps.total} total, {deps.outdated} outdated
|
||||
</span>
|
||||
{vulnCount > 0 ? (
|
||||
<span class="text-[10px] font-medium text-red-500">
|
||||
⚠ {vulnCount} vulnerabilities
|
||||
</span>
|
||||
) : (
|
||||
<span class="text-[10px] font-medium text-emerald-500">
|
||||
✓ no vulnerabilities
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* Lighthouse scores */}
|
||||
{data.lighthouse && (
|
||||
<div class="mt-2 flex items-center gap-3">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue