开源前加固认证与部署契约:加密随机 JWT、改密事务吊销、登录 CSRF,并禁止生产回落 localhost。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-23 02:07:34 +08:00
parent 6f054a903c
commit 3b550f2124
24 changed files with 372 additions and 355 deletions

View File

@@ -1,17 +1,56 @@
"use client";
import {useEffect,useState} from "react";
import {usePathname} from "next/navigation";
import {apiOperations} from "@/lib/api";
export default function OperationalBanner(){
const path=usePathname();
const [notice,setNotice]=useState("");
useEffect(()=>{
let active=true;
const refresh=()=>{if(document.visibilityState!=="visible")return; void apiOperations<{maintenance:{mode:string;title:string;message:string}} >("/api/site-state").then(s=>{if(active)setNotice(s.maintenance.mode==="readonly" ? (s.maintenance.message || "站点暂时只读,可以浏览,暂不能提交或修改内容。") : "");}).catch(()=>{});};
refresh();const timer=setInterval(refresh,30000);
document.addEventListener("visibilitychange",refresh);
return ()=>{active=false;clearInterval(timer);document.removeEventListener("visibilitychange",refresh);};
},[path]);
if(path.startsWith("/admin") || !notice)return null;
return <div role="status" className="panel mx-auto my-3 w-full max-w-5xl p-4 text-sm"><strong>只读模式:</strong>{notice}</div>;
import { useEffect, useState } from "react";
import { usePathname } from "next/navigation";
import { apiOperations } from "@/lib/api";
import { realtime, RT_SETTINGS_CHANGED } from "@/lib/realtime";
import {
VISIBLE_RECONCILE_GAP_MS,
onDocumentVisible,
} from "@/lib/visibilityReconcile";
/** 只读维护条:WS settings:changed 优先,回前台再校准(无定时轮询) */
export default function OperationalBanner() {
const path = usePathname();
const [notice, setNotice] = useState("");
useEffect(() => {
let active = true;
const apply = (mode: string | undefined, message: string | undefined) => {
if (!active) return;
setNotice(
mode === "readonly"
? message || "站点暂时只读,可以浏览,暂不能提交或修改内容。"
: ""
);
};
const refresh = () => {
if (document.visibilityState !== "visible") return;
void apiOperations<{
maintenance: { mode: string; title: string; message: string };
}>("/api/site-state")
.then((s) => apply(s.maintenance?.mode, s.maintenance?.message))
.catch(() => {});
};
refresh();
const offPush = realtime.on(RT_SETTINGS_CHANGED, () => refresh());
const offVisible = onDocumentVisible(refresh, VISIBLE_RECONCILE_GAP_MS);
return () => {
active = false;
offPush();
offVisible();
};
}, [path]);
if (path.startsWith("/admin") || !notice) return null;
return (
<div role="status" className="panel mx-auto my-3 w-full max-w-5xl p-4 text-sm">
<strong>只读模式:</strong>
{notice}
</div>
);
}

View File

@@ -378,6 +378,7 @@ function ActiveUsers({ users, allowComments }: { users: ActiveUser[]; allowComme
/* ---------- 友情链接 ---------- */
function FriendLinks() {
if (FRIEND_LINKS.length === 0) return null;
return (
<section className="j13-module" aria-label="友情链接">
<p className="j13-module-title mb-3">