Initial commit: homelab dashboard

Elixir + Phoenix dashboard displaying public and internal homelab
services. Internal services are shown only when the request originates
from the local network, detected server-side via conn.remote_ip with
Traefik trusted as a proxy (RemoteIp plug).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
adyrem
2026-05-18 16:02:29 +02:00
commit f92c2247a0
47 changed files with 3874 additions and 0 deletions
@@ -0,0 +1,76 @@
<Layouts.flash_group flash={@flash} />
<div class="min-h-screen bg-base-100">
<header class="border-b border-base-300 bg-base-200">
<div class="mx-auto max-w-5xl px-6 py-6 flex items-center justify-between">
<div class="flex items-center gap-3">
<span class="hero-home h-7 w-7 text-primary"></span>
<h1 class="text-xl font-bold tracking-tight">Homelab Dashboard</h1>
</div>
<Layouts.theme_toggle />
</div>
</header>
<main class="mx-auto max-w-5xl px-6 py-10 space-y-10">
<section>
<h2 class="text-xs font-semibold uppercase tracking-widest text-base-content/50 mb-4">
Public Services
</h2>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
<%= for service <- @public_services do %>
<a
href={service.url}
target="_blank"
rel="noopener noreferrer"
class="group flex items-start gap-4 rounded-box bg-base-200 p-5 transition hover:bg-base-300 hover:shadow-md"
>
<div class="flex h-10 w-10 shrink-0 items-center justify-center rounded-btn bg-primary/10 text-primary group-hover:bg-primary/20 transition">
<span class={[service.icon, "h-5 w-5"]}></span>
</div>
<div class="min-w-0">
<div class="flex items-center gap-2">
<span class="font-semibold"><%= service.name %></span>
<span class="hero-arrow-top-right-on-square h-3.5 w-3.5 text-base-content/30 group-hover:text-primary transition"></span>
</div>
<p class="mt-0.5 text-sm text-base-content/60 leading-snug"><%= service.description %></p>
</div>
</a>
<% end %>
</div>
</section>
<%= if @show_internal do %>
<section>
<div class="flex items-center gap-3 mb-4">
<h2 class="text-xs font-semibold uppercase tracking-widest text-base-content/50">
Internal Services
</h2>
<span class="badge badge-success badge-sm gap-1">
<span class="inline-block h-1.5 w-1.5 rounded-full bg-success-content"></span>
Local network
</span>
</div>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
<%= for service <- @internal_services do %>
<a
href={service.url}
target="_blank"
rel="noopener noreferrer"
class="group flex items-start gap-4 rounded-box bg-base-200 p-5 transition hover:bg-base-300 hover:shadow-md"
>
<div class="flex h-10 w-10 shrink-0 items-center justify-center rounded-btn bg-secondary/10 text-secondary group-hover:bg-secondary/20 transition">
<span class={[service.icon, "h-5 w-5"]}></span>
</div>
<div class="min-w-0">
<div class="flex items-center gap-2">
<span class="font-semibold"><%= service.name %></span>
<span class="hero-arrow-top-right-on-square h-3.5 w-3.5 text-base-content/30 group-hover:text-secondary transition"></span>
</div>
<p class="mt-0.5 text-sm text-base-content/60 leading-snug"><%= service.description %></p>
</div>
</a>
<% end %>
</div>
</section>
<% end %>
</main>
</div>