speed up admin ui polling and payloads
This commit is contained in:
+79
-26
@@ -1,4 +1,4 @@
|
||||
import { FormEvent, useEffect, useState } from "react";
|
||||
import { FormEvent, startTransition, useEffect, useState } from "react";
|
||||
|
||||
type PageKey = "overview" | "requests" | "profiles" | "rules" | "logs";
|
||||
type Tone = "" | "success" | "error";
|
||||
@@ -222,6 +222,20 @@ const api = {
|
||||
restore: "/__codex_retry_gateway/api/restore",
|
||||
};
|
||||
|
||||
const REQUEST_PAGE_SIZE = 40;
|
||||
const LOG_PAGE_SIZE = 200;
|
||||
|
||||
const zhNumberFormatter = new Intl.NumberFormat("zh-CN");
|
||||
const zhTimestampFormatter = new Intl.DateTimeFormat("zh-CN", {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hourCycle: "h23",
|
||||
});
|
||||
|
||||
type ProfileProbePayload = {
|
||||
ok: boolean;
|
||||
profile?: string;
|
||||
@@ -294,7 +308,7 @@ const defaultProfileForm: ProfileFormState = {
|
||||
|
||||
function numberFormat(value: unknown) {
|
||||
return typeof value === "number" && Number.isFinite(value)
|
||||
? new Intl.NumberFormat("zh-CN").format(value)
|
||||
? zhNumberFormatter.format(value)
|
||||
: "-";
|
||||
}
|
||||
|
||||
@@ -303,7 +317,7 @@ function timestamp(value?: string | null) {
|
||||
return "-";
|
||||
}
|
||||
const date = new Date(value);
|
||||
return Number.isNaN(date.getTime()) ? value : date.toLocaleString("zh-CN", { hour12: false });
|
||||
return Number.isNaN(date.getTime()) ? value : zhTimestampFormatter.format(date);
|
||||
}
|
||||
|
||||
function durationSeconds(value?: number | null) {
|
||||
@@ -482,6 +496,7 @@ export default function App() {
|
||||
});
|
||||
const [status, setStatus] = useState<StatusPayload | null>(null);
|
||||
const [requests, setRequests] = useState<RequestEntry[]>([]);
|
||||
const [requestsTotal, setRequestsTotal] = useState(0);
|
||||
const [requestsMeta, setRequestsMeta] = useState("正在读取请求记录...");
|
||||
const [profiles, setProfiles] = useState<Profile[]>([]);
|
||||
const [profilesMeta, setProfilesMeta] = useState("正在读取 profiles...");
|
||||
@@ -490,6 +505,7 @@ export default function App() {
|
||||
const [latestLogSeq, setLatestLogSeq] = useState(0);
|
||||
const [requestQuery, setRequestQuery] = useState("");
|
||||
const [requestFilter, setRequestFilter] = useState("all");
|
||||
const [requestLimit, setRequestLimit] = useState(REQUEST_PAGE_SIZE);
|
||||
const [ruleForm, setRuleForm] = useState<RuleFormState>(ruleFormFromStatus(null));
|
||||
const [profileForm, setProfileForm] = useState<ProfileFormState>(defaultProfileForm);
|
||||
const [ruleMessage, setRuleMessage] = useState<MessageState>({ text: "", tone: "" });
|
||||
@@ -513,16 +529,18 @@ export default function App() {
|
||||
|
||||
async function loadStatus(refreshRuleForm = false) {
|
||||
const payload = await fetchJson<StatusPayload>(api.status);
|
||||
setStatus(payload);
|
||||
if (refreshRuleForm) {
|
||||
setRuleForm(ruleFormFromStatus(payload));
|
||||
setProfileForm(profileFormFromStatus(payload));
|
||||
}
|
||||
startTransition(() => {
|
||||
setStatus(payload);
|
||||
if (refreshRuleForm) {
|
||||
setRuleForm(ruleFormFromStatus(payload));
|
||||
setProfileForm(profileFormFromStatus(payload));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function loadRequests() {
|
||||
async function loadRequests(limitOverride = requestLimit) {
|
||||
const url = new URL(api.requests, window.location.origin);
|
||||
url.searchParams.set("limit", "100");
|
||||
url.searchParams.set("limit", String(limitOverride));
|
||||
if (requestQuery.trim()) {
|
||||
url.searchParams.set("query", requestQuery.trim());
|
||||
}
|
||||
@@ -531,36 +549,47 @@ export default function App() {
|
||||
}
|
||||
const payload = await fetchJson<RequestsPayload>(url.toString());
|
||||
const entries = payload.entries || [];
|
||||
setRequests(entries);
|
||||
setRequestsMeta(`查询命中 ${payload.total_entries ?? entries.length} 条,最新序号 ${payload.latest_seq ?? 0}。`);
|
||||
startTransition(() => {
|
||||
setRequests(entries);
|
||||
setRequestsTotal(payload.total_entries ?? entries.length);
|
||||
setRequestsMeta(
|
||||
`查询命中 ${payload.total_entries ?? entries.length} 条,当前展示最近 ${entries.length} 条,最新序号 ${payload.latest_seq ?? 0}。`,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async function loadProfiles() {
|
||||
const payload = await fetchJson<ProfilesPayload>(api.profiles);
|
||||
const items = payload.profiles || [];
|
||||
setProfiles(items);
|
||||
setProfilesMeta(`目录:${payload.profiles_dir || "-"};当前运行:${payload.active_profile || "-"}。`);
|
||||
startTransition(() => {
|
||||
setProfiles(items);
|
||||
setProfilesMeta(`目录:${payload.profiles_dir || "-"};当前运行:${payload.active_profile || "-"}。`);
|
||||
});
|
||||
}
|
||||
|
||||
async function loadLogs(incremental = false) {
|
||||
const url = new URL(api.logs, window.location.origin);
|
||||
if (incremental && latestLogSeq > 0) {
|
||||
url.searchParams.set("since_seq", String(latestLogSeq));
|
||||
} else {
|
||||
url.searchParams.set("limit", String(LOG_PAGE_SIZE));
|
||||
}
|
||||
const payload = await fetchJson<LogsPayload>(url.toString());
|
||||
const rendered = (payload.entries || [])
|
||||
.map((entry) => `${entry.at || "-"} ${entry.message || ""}`)
|
||||
.join("\n");
|
||||
setLogs((current) => {
|
||||
if (!incremental || latestLogSeq === 0) {
|
||||
return rendered || "当前还没有日志。";
|
||||
startTransition(() => {
|
||||
setLogs((current) => {
|
||||
if (!incremental || latestLogSeq === 0) {
|
||||
return rendered || "当前还没有日志。";
|
||||
}
|
||||
return rendered ? `${current.trim()}\n${rendered}` : current;
|
||||
});
|
||||
setLogsMeta(`已载入 ${payload.total_entries ?? payload.entries?.length ?? 0} 条日志,最新序号 ${payload.latest_seq ?? latestLogSeq}。`);
|
||||
if (typeof payload.latest_seq === "number") {
|
||||
setLatestLogSeq(payload.latest_seq);
|
||||
}
|
||||
return rendered ? `${current.trim()}\n${rendered}` : current;
|
||||
});
|
||||
setLogsMeta(`已载入 ${payload.total_entries ?? payload.entries?.length ?? 0} 条日志,最新序号 ${payload.latest_seq ?? latestLogSeq}。`);
|
||||
if (typeof payload.latest_seq === "number") {
|
||||
setLatestLogSeq(payload.latest_seq);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadPageData(targetPage: PageKey, { incrementalLogs = true } = {}) {
|
||||
@@ -617,12 +646,24 @@ export default function App() {
|
||||
|
||||
useEffect(() => {
|
||||
const timer = window.setInterval(() => {
|
||||
refreshLiveData().catch((error) => {
|
||||
loadStatus(false).catch((error) => {
|
||||
setRuleMessage({ text: error?.message || String(error), tone: "error" });
|
||||
});
|
||||
}, 10000);
|
||||
return () => window.clearInterval(timer);
|
||||
}, [restoreRequested, switchingTo]);
|
||||
|
||||
useEffect(() => {
|
||||
if (page !== "requests" && page !== "logs") {
|
||||
return;
|
||||
}
|
||||
const timer = window.setInterval(() => {
|
||||
loadPageData(page, { incrementalLogs: true }).catch((error) => {
|
||||
setRuleMessage({ text: error?.message || String(error), tone: "error" });
|
||||
});
|
||||
}, 2500);
|
||||
return () => window.clearInterval(timer);
|
||||
}, [page, requestQuery, requestFilter, latestLogSeq, restoreRequested, switchingTo]);
|
||||
}, [page, requestQuery, requestFilter, latestLogSeq, requestLimit]);
|
||||
|
||||
useEffect(() => {
|
||||
if (page === "overview" || page === "rules") {
|
||||
@@ -676,17 +717,21 @@ export default function App() {
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setRequestLimit(REQUEST_PAGE_SIZE);
|
||||
}, [requestQuery, requestFilter]);
|
||||
|
||||
useEffect(() => {
|
||||
if (page !== "requests") {
|
||||
return;
|
||||
}
|
||||
const timer = window.setTimeout(() => {
|
||||
loadRequests().catch((error) => {
|
||||
loadRequests(requestLimit).catch((error) => {
|
||||
setRuleMessage({ text: error?.message || String(error), tone: "error" });
|
||||
});
|
||||
}, 180);
|
||||
return () => window.clearTimeout(timer);
|
||||
}, [page, requestQuery, requestFilter]);
|
||||
}, [page, requestQuery, requestFilter, requestLimit]);
|
||||
|
||||
async function saveProfile(event: FormEvent) {
|
||||
event.preventDefault();
|
||||
@@ -987,6 +1032,14 @@ export default function App() {
|
||||
<button className="secondary" type="button" onClick={() => loadRequests()}>
|
||||
刷新请求
|
||||
</button>
|
||||
<button
|
||||
className="ghost"
|
||||
type="button"
|
||||
disabled={requests.length >= requestsTotal && requestsTotal > 0}
|
||||
onClick={() => setRequestLimit((current) => current + REQUEST_PAGE_SIZE)}
|
||||
>
|
||||
更多
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user