fix(sidebar): omit changelog badge for anon visitors + clarify CLAUDE.md auth gate (t-paliad-035)

The marketing landing (`/`) renders the same Sidebar as protected pages, so
`initChangelogBadge()` was firing `GET /api/changelog/unseen-count` on every
anon visit and getting 401. Cosmetic noise + wasted round-trip.

Add an `authenticated` prop to Sidebar (defaults to true, no behavior change
on protected pages) and pass `false` from `renderIndex()`. The badge `<a>`
is omitted server-side; the existing `if (!badge) return` guard in
sidebar.ts naturally skips the fetch when the element is absent — no
client change needed.

Also append a clarifying note under the env-var table in .claude/CLAUDE.md:
"work without DB" doesn't mean "ungated for anon". The knowledge-platform
routes (Kostenrechner, Glossar, etc.) are still behind the auth gate; only
`/`, `/login`, `/logout`, and `/assets/*` are public. Misread by the smoke
tester briefer; spelled out now to prevent recurrence.
This commit is contained in:
m
2026-04-25 23:09:36 +02:00
parent 0ad2e86945
commit 83d5973dd6
3 changed files with 17 additions and 7 deletions

View File

@@ -48,6 +48,8 @@ Paliad — the patent paladin. All-in-one patent practice platform for HLC (form
| `SMTP_HOST` / `SMTP_PORT` / `SMTP_USERNAME` / `SMTP_PASSWORD` / `SMTP_FROM` / `SMTP_FROM_NAME` / `SMTP_USE_TLS` | for email | SMTP credentials for Paliad's transactional mail (reminders, invitations). Port 465 uses implicit TLS. `MailService` silently no-ops when any required var is missing — the server still boots for knowledge-platform-only deployments. |
| `ANTHROPIC_API_KEY` | not used today | Reserved for Phase H (AI Frist-Extraktion) which is deferred per m's 2026-04-16 decision. Do not set. |
> *Note on `DATABASE_URL`:* "Work without DB" ≠ "ungated". All knowledge-platform routes (Kostenrechner, Glossar, Links, Gebührentabellen, Checklisten, Gerichte, Downloads) are still behind the auth gate (302 to `/login` for anon visitors); only `/`, `/login`, `/logout`, and `/assets/*` are public. The `gateOnboarded` middleware additionally blocks unonboarded users from app pages but does NOT gate the knowledge-platform pages.
## Infrastructure
- **Gitea:** `mAi/paliad` on mgit.msbls.de (renamed from mAi/patholo — auto-redirects)

View File

@@ -25,6 +25,12 @@ const ICON_USERS = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" s
interface SidebarProps {
currentPath: string;
// authenticated defaults to true; pass false on anon-facing pages (the
// marketing landing) so we don't render auth-only affordances that would
// fire 401s. Currently gates only the changelog badge link — the badge
// client (sidebar.ts initChangelogBadge) early-returns when the link is
// absent, so there is no client change needed.
authenticated?: boolean;
}
function navItem(href: string, icon: string, i18nKey: string, label: string, currentPath: string): string {
@@ -60,7 +66,7 @@ function group(i18nKey: string, label: string, children: string): string {
);
}
export function Sidebar({ currentPath }: SidebarProps): string {
export function Sidebar({ currentPath, authenticated = true }: SidebarProps): string {
return (
<Fragment>
<aside className="sidebar">
@@ -129,11 +135,13 @@ export function Sidebar({ currentPath }: SidebarProps): string {
<div className="sidebar-spacer" />
<div className="sidebar-bottom">
<a href="/changelog" className={`sidebar-item sidebar-changelog${currentPath === "/changelog" ? " active" : ""}`} id="sidebar-changelog-link">
<span className="sidebar-icon" dangerouslySetInnerHTML={{ __html: ICON_SPARKLE }} />
<span className="sidebar-label" data-i18n="nav.neuigkeiten">Neuigkeiten</span>
<span className="sidebar-badge" id="sidebar-changelog-badge" style="display:none" aria-hidden="true" />
</a>
{authenticated ? (
<a href="/changelog" className={`sidebar-item sidebar-changelog${currentPath === "/changelog" ? " active" : ""}`} id="sidebar-changelog-link">
<span className="sidebar-icon" dangerouslySetInnerHTML={{ __html: ICON_SPARKLE }} />
<span className="sidebar-label" data-i18n="nav.neuigkeiten">Neuigkeiten</span>
<span className="sidebar-badge" id="sidebar-changelog-badge" style="display:none" aria-hidden="true" />
</a>
) : ""}
<button type="button" className="sidebar-item sidebar-invite-btn" id="sidebar-invite-btn">
<span className="sidebar-icon" dangerouslySetInnerHTML={{ __html: ICON_MAIL }} />
<span className="sidebar-label" data-i18n="invite.button">Kolleg:in einladen</span>

View File

@@ -23,7 +23,7 @@ export function renderIndex(): string {
<link rel="stylesheet" href="/assets/global.css" />
</head>
<body className="has-sidebar">
<Sidebar currentPath="/" />
<Sidebar currentPath="/" authenticated={false} />
<main>
<section className="hero">