移除旧版 HTML 模板与兼容层,并完善私信、举报、媒体存储与 SEO。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-03 16:37:11 +08:00
parent 060b7707cb
commit 48db333272
121 changed files with 11147 additions and 3225 deletions

View File

@@ -4,21 +4,53 @@ import type { SiteBranding } from '../api/types';
export const DEFAULT_BRANDING: SiteBranding = {
name: '姜十三论坛',
name_en: 'Jiang13 Forum',
slogan: '拾三一隅,自在交流',
description: '',
keywords: '',
logo_mark: '姜',
logo: '',
favicon: '',
og_image: '',
icp_beian: '',
icp_beian_url: 'https://beian.miit.gov.cn/',
friend_links: [],
};
let cached: SiteBranding | null = null;
declare global {
interface Window {
/** 服务端注入的首屏品牌配置(见 embed_static SPA HTML) */
__J13_BRANDING__?: Partial<SiteBranding>;
}
}
/** SEO / 首页展示用简介:优先 description,其次 slogan */
export function siteMetaDescription(brand: SiteBranding): string {
const d = brand.description?.trim() ?? '';
if (d) return d;
return brand.slogan?.trim() ?? '';
}
/** 从服务端注入的 boot 数据同步初始化,避免首屏闪默认站名 */
function readBootBranding(): SiteBranding | null {
try {
const boot = window.__J13_BRANDING__;
if (!boot || typeof boot !== 'object') return null;
const name = typeof boot.name === 'string' ? boot.name.trim() : '';
if (!name) return null;
return { ...DEFAULT_BRANDING, ...boot, name };
} catch {
return null;
}
}
let cached: SiteBranding | null = readBootBranding();
let inflight: Promise<SiteBranding> | null = null;
let cacheEpoch = 0;
const listeners = new Set<() => void>();
function fetchBranding(): Promise<SiteBranding> {
if (cached) return Promise.resolve(cached);
if (inflight) return inflight;
// 有 boot/缓存时首屏已可用;仍请求 API 以同步最新配置
inflight = api.siteBranding()
.then(b => {
cached = { ...DEFAULT_BRANDING, ...b };
@@ -37,8 +69,11 @@ export function formatDocumentTitle(brand: SiteBranding): string {
}
function applyDocumentBrand(brand: SiteBranding) {
const title = formatDocumentTitle(brand);
if (document.title !== title) document.title = title;
// 页面级 SEO hook 已接管标题时,勿覆盖
if (!document.documentElement.hasAttribute('data-j13-seo')) {
const title = formatDocumentTitle(brand);
if (document.title !== title) document.title = title;
}
let link = document.querySelector<HTMLLinkElement>('link[rel="icon"]');
if (brand.favicon) {
@@ -53,6 +88,11 @@ function applyDocumentBrand(brand: SiteBranding) {
}
}
/** 同步读取已缓存的品牌配置(供 SEO 等非 hook 场景) */
export function getCachedSiteBranding(): SiteBranding {
return cached ?? DEFAULT_BRANDING;
}
/** 获取站点品牌配置(名称、Logo 等) */
export function useSiteBranding() {
const [branding, setBranding] = useState<SiteBranding>(cached ?? DEFAULT_BRANDING);