Files
jiang13-bbs/frontend/lib/cookies.ts
freefire 1d862a0dd3 feat: 站点媒体库与移动端底部导航,服务测试补强
- 新增媒体库:media_library/media_thumbs 服务(WebP 缩略图)、管理端 media 页面
- 移动端底部导航 MobileTabBar 替换 MobilePostBar
- 旧数据导入增强与测试、路由与登录会话/板块侧边栏测试补强
- site-doc 组件精简(移除 Breadcrumb),文档新增迁移公告说明
2026-09-27 02:36:11 +08:00

74 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 认证 cookie 名(前后端必须严格一致):
// 与后端 ConfigureCookieNames(!DevMode) 对齐——显式 DEV_MODE=true|1 禁用 __Host-;
// 显式 DEV_MODE=false|0 启用;未设置时回退 NODE_ENV=production(官方镜像两者同时满足)。
function hostCookiePrefix(): string {
const dm = (process.env.DEV_MODE || "").trim().toLowerCase();
if (dm === "true" || dm === "1") return "";
if (dm === "false" || dm === "0") return "__Host-";
return process.env.NODE_ENV === "production" ? "__Host-" : "";
}
const HOST_PREFIX = hostCookiePrefix();
export const TOKEN_COOKIE = `${HOST_PREFIX}j13_token`;
export const REFRESH_COOKIE = `${HOST_PREFIX}j13_refresh`;
export const CSRF_COOKIE = `${HOST_PREFIX}j13_csrf`;
/** 密码隐藏块解锁 cookie 前缀(与 backend HidePasswordCookie 一致) */
export const HIDE_PWD_COOKIE_PREFIX = "j13_hp_";
type CookieReader = {
get(name: string): { value: string } | undefined;
};
type CookieStoreLike = CookieReader & {
getAll?: () => { name: string; value: string }[];
};
// 构造转发给后端的最小 Cookie 头:SSR 公开/鉴权接口只需要 access token 来
// 识别用户(填充 liked 等状态),不应把 refresh token、主题等无关 cookie 全量转发。
// 返回空串表示当前无登录凭据,调用方应省略 Cookie 头。
export function authCookieHeader(store: CookieReader): string {
const token = store.get(TOKEN_COOKIE);
return token ? `${TOKEN_COOKIE}=${token.value}` : "";
}
/**
* 登录设备列表 SSR:必须同时转发 refresh cookie,才能识别「当前会话」。
* 其它 SSR 接口不要用这个,避免把 refresh token 带到无关后端请求。
*/
export function sessionCookieHeader(store: CookieReader): string {
const parts: string[] = [];
const token = store.get(TOKEN_COOKIE);
if (token) parts.push(`${TOKEN_COOKIE}=${token.value}`);
const refresh = store.get(REFRESH_COOKIE);
if (refresh) parts.push(`${REFRESH_COOKIE}=${refresh.value}`);
return parts.join("; ");
}
/**
* 帖子详情 SSR:access token + 密码隐藏块解锁 cookie。
* 解锁 cookie 为 HttpOnly,必须随 SSR 转发,否则 refresh 后密码块会再次锁上。
*/
export function postViewerCookieHeader(store: CookieStoreLike): string {
const parts: string[] = [];
const auth = authCookieHeader(store);
if (auth) parts.push(auth);
if (typeof store.getAll === "function") {
for (const c of store.getAll()) {
if (c.name.startsWith(HIDE_PWD_COOKIE_PREFIX)) {
parts.push(`${c.name}=${c.value}`);
}
}
}
return parts.join("; ");
}
// 客户端判断浏览器是否可能持有登录态:j13_csrf 非 HttpOnly 可读,
// 生命周期与 refresh 一致,适合作为挂载后静默校正的触发信号。
export function hasAuthCookieHint(): boolean {
if (typeof document === "undefined") return false;
const escaped = CSRF_COOKIE.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
return new RegExp(`(?:^|;\\s*)${escaped}=`).test(document.cookie);
}