Files
freefire ff2ab286fb feat: 书库导入导出/图片变体/书籍搜索/小组件运行时等
新增:
- 书库导入导出(library_import/library_export)及测试
- 图片变体生成(image_variants)与响应式图片(responsiveImage)
- 书籍搜索(bookSearch)+ BookSearch/LibrarySearchGrid 组件
- 小组件运行时(widgetRuntime)与静态检查(widgetLint)
- 上传缓存中间件(upload_cache)与字体 CSS 提取脚本

其它:
- 后端 handlers/services 全量调整
- 前端页面、组件、库函数与配置更新
2026-10-01 03:06:05 +08:00

87 lines
2.9 KiB
TypeScript

import type { Metadata } from "next";
import { cookies } from "next/headers";
import { fetchFriendLinks, fetchSiteState, type FriendLink } from "@/lib/api";
import { authCookieHeader } from "@/lib/cookies";
import { brandAliasPath } from "@/lib/brand";
import { getMeCached, getPublicSettingsCached } from "@/lib/serverData";
import { hasPerm, PERMS } from "@/lib/roles";
import { requestCanonical } from "@/lib/canonicalMeta";
import { resolveSiteOrigin } from "@/lib/siteUrl";
import LinksBoard from "./LinksBoard";
export async function generateMetadata(): Promise<Metadata> {
return {
title: "友情链接",
description: "本站友情链接收录与申请",
...(await requestCanonical("/links")),
};
}
export default async function FriendLinksPage() {
const cookie = authCookieHeader(await cookies());
let links: FriendLink[] = [];
let error = "";
try {
const res = await fetchFriendLinks(cookie || undefined);
links = res.links ?? [];
} catch (e) {
error = e instanceof Error ? e.message : "加载友情链接失败";
}
const [settings, siteState, me] = await Promise.all([
getPublicSettingsCached(),
fetchSiteState(),
getMeCached(cookie || undefined),
]);
const isLoggedIn = !!me.user;
const canManage = !!me.user && hasPerm(me.user, PERMS.ANNOUNCEMENTS);
// 站点 LOGO 用固定对外地址(换图不变链,对方收藏的地址永不失效),补全为绝对地址供申请人直接复制
const siteBase = siteState.site_url?.trim() || resolveSiteOrigin() || "";
const logoAlias = settings.logo_light_url
? brandAliasPath("logo_light")
: settings.favicon_url
? brandAliasPath("favicon")
: "";
const siteLogo = logoAlias && siteBase ? new URL(logoAlias, siteBase).toString() : "";
return (
<div className="mx-auto w-full max-w-[1200px] px-4 sm:px-6 py-8">
{/* 标题 + 申请须知 */}
<header className="text-center">
<h1
className="text-[26px] sm:text-[30px] font-extrabold tracking-tight inline-flex flex-col items-center"
style={{ color: "var(--ink)" }}
>
友情链接
<span
className="mt-1.5 h-[3px] w-10 rounded-full"
style={{ background: "var(--accent)" }}
aria-hidden
/>
</h1>
</header>
{/* 搜索 + 卡片墙 + 我的申请 + 申请弹窗 */}
<div className="mt-5">
{error ? (
<p
className="rounded-lg px-3 py-2 text-[13px] text-center"
style={{ background: "var(--panel-2)", color: "var(--ink)", border: "1px solid var(--line)" }}
>
{error}
</p>
) : (
<LinksBoard
links={links}
isLoggedIn={isLoggedIn}
canManage={canManage}
siteName={settings.site_name}
siteUrl={siteState.site_url}
siteLogo={siteLogo}
/>
)}
</div>
</div>
);
}