frontend: add bookmark management and homepage navigation

Admin-only /bookmarks page for managing entries; homepage now renders
public bookmarks as a category-grouped navigation grid (empty state
links admin to the manager). Dashboard gains a recent-bookmarks card,
dock and main layout get a bookmark entry for admins, and the
middleware protects /bookmarks.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-03 01:51:55 +08:00
parent ffecc9451d
commit 37cecaa1ce
8 changed files with 625 additions and 42 deletions

View File

@@ -5,11 +5,35 @@ import { HomePageClient } from "./home-page-client";
const SERVER_API_URL = process.env.SERVER_API_URL || "http://backend:8080";
const hasKeycloak = !!process.env.AUTH_KEYCLOAK_ISSUER;
interface Bookmark {
id: number;
title: string;
url: string;
description: string;
icon: string;
category: string;
}
async function fetchPublicBookmarks(): Promise<Bookmark[]> {
try {
const res = await fetch(`${SERVER_API_URL}/api/bookmarks/public`, {
next: { revalidate: 0 },
});
if (!res.ok) return [];
const data = await res.json();
return data.bookmarks || [];
} catch {
return [];
}
}
export default async function HomePage() {
const session = await auth();
const isAuthenticated = !!session?.user;
const isAdmin = (session?.user as any)?.role === "admin";
let healthText = "无法连接到后端服务";
let bookmarks: Bookmark[] = [];
try {
const res = await fetch(`${SERVER_API_URL}/api/health`, {
@@ -20,16 +44,20 @@ export default async function HomePage() {
} else {
healthText = `后端异常: ${res.status}`;
}
} catch (err) {
} catch {
healthText = "后端连接失败";
}
bookmarks = await fetchPublicBookmarks();
return (
<Suspense>
<HomePageClient
isAuthenticated={isAuthenticated}
isAdmin={isAdmin}
healthText={healthText}
hasKeycloak={hasKeycloak}
bookmarks={bookmarks}
/>
</Suspense>
);