feat(hide): 密码可见隐藏块,发帖双栏预览与门禁体验修复

增加密码解锁与游客签名 cookie;发帖页对齐 1440 并默认双栏预览;修复 locked 解析、按钮对比度与回复聚焦。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-17 05:46:42 +08:00
parent 01e2fd05ca
commit c44b0efa7d
23 changed files with 911 additions and 132 deletions

View File

@@ -7,10 +7,17 @@ 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 头。
@@ -19,6 +26,24 @@ export function authCookieHeader(store: CookieReader): string {
return token ? `${TOKEN_COOKIE}=${token.value}` : "";
}
/**
* 帖子详情 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 {