feat: add landing page at / with navigation links

Route GET / to a landing page with the Porchlight logo, tagline,
and card-style navigation links to My Account and Administration.
This commit is contained in:
Johan Lundberg 2026-02-19 15:38:09 +01:00
parent cedf2a65e2
commit 0435b81c5a
No known key found for this signature in database
GPG key ID: A6C152738D03C7D1
4 changed files with 95 additions and 0 deletions

View file

@ -146,4 +146,8 @@ def create_app(settings: Settings | None = None) -> FastAPI:
async def health() -> dict[str, str]:
return {"status": "ok"}
@app.get("/")
async def landing(request: Request): # type: ignore[no-untyped-def]
return templates.TemplateResponse(request, "index.html")
return app

View file

@ -530,6 +530,66 @@ li {
border: 0;
}
/* ---------- Landing page ---------- */
.landing {
text-align: center;
padding-top: var(--sp-8);
}
.landing-logo {
width: 6rem;
height: 6rem;
margin-bottom: var(--sp-6);
}
.landing h1 {
margin-bottom: var(--sp-3);
}
.landing-blurb {
color: var(--fg-muted);
font-size: var(--font-size-lg);
max-width: 30rem;
margin: 0 auto var(--sp-8);
}
.landing-nav {
display: flex;
flex-direction: column;
gap: var(--sp-4);
max-width: 24rem;
margin: 0 auto;
}
.landing-link {
display: block;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: var(--sp-4) var(--sp-6);
text-decoration: none;
text-align: left;
transition: border-color 0.15s ease;
}
.landing-link:hover {
border-color: var(--accent);
color: inherit;
}
.landing-link strong {
display: block;
color: var(--fg);
font-size: var(--font-size-base);
margin-bottom: var(--sp-1);
}
.landing-link span {
color: var(--fg-muted);
font-size: var(--font-size-sm);
}
/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {

View file

@ -0,0 +1,19 @@
{% extends "base.html" %}
{% block content %}
<div class="landing">
<img class="landing-logo" src="/static/logo.svg" alt="" width="96" height="96">
<h1>Porchlight</h1>
<p class="landing-blurb">OpenID Connect Provider with user management. Manage your identity, credentials, and connected applications.</p>
<nav class="landing-nav" aria-label="Quick links">
<a class="landing-link" href="/manage/profile">
<strong>My Account</strong>
<span>Manage your profile and credentials</span>
</a>
<a class="landing-link" href="/admin/users">
<strong>Administration</strong>
<span>Manage users and settings</span>
</a>
</nav>
</div>
{% endblock %}

View file

@ -28,6 +28,18 @@ async def test_app_has_repos_on_state(client: AsyncClient) -> None:
assert isinstance(app.state.magic_link_repo, MagicLinkRepository)
async def test_landing_page(client: AsyncClient) -> None:
response = await client.get("/")
assert response.status_code == 200
assert "text/html" in response.headers["content-type"]
body = response.text
assert "<h1>Porchlight</h1>" in body
assert "/manage/profile" in body
assert "/admin/users" in body
assert "My Account" in body
assert "Administration" in body
async def test_dependency_functions() -> None:
from unittest.mock import MagicMock