security(docs): prevent stale projection rendering
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { Link, useLocation } from "react-router-dom";
|
import { Link, useLocation } from "react-router-dom";
|
||||||
import { ChevronDown, ChevronRight, RefreshCw } from "lucide-react";
|
import { ChevronDown, ChevronRight, RefreshCw } from "lucide-react";
|
||||||
import {
|
import {
|
||||||
@@ -67,20 +67,29 @@ export default function DocsPage({ settings }: { settings: ApiSettings }) {
|
|||||||
const [context, setContext] = useState<DocsContext | null>(null);
|
const [context, setContext] = useState<DocsContext | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
|
const loadSequence = useRef(0);
|
||||||
const [documentationType, setDocumentationType] = useState<DocumentationType>(() => documentationTypeFromSearch(location.search));
|
const [documentationType, setDocumentationType] = useState<DocumentationType>(() => documentationTypeFromSearch(location.search));
|
||||||
const [expandedNodes, setExpandedNodes] = useState<Set<string>>(() => new Set());
|
const [expandedNodes, setExpandedNodes] = useState<Set<string>>(() => new Set());
|
||||||
const locale = localeFromSearch(location.search) ?? language;
|
const locale = localeFromSearch(location.search) ?? language;
|
||||||
const adminDocs = documentationType === "admin";
|
const adminDocs = documentationType === "admin";
|
||||||
|
|
||||||
async function load() {
|
async function load() {
|
||||||
|
const sequence = ++loadSequence.current;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError("");
|
setError("");
|
||||||
|
setContext(null);
|
||||||
try {
|
try {
|
||||||
setContext(await fetchDocsContext(settings, { documentationType, locale }));
|
const nextContext = await fetchDocsContext(settings, { documentationType, locale });
|
||||||
|
if (sequence !== loadSequence.current) return;
|
||||||
|
if (nextContext.actor.documentation_type !== documentationType) {
|
||||||
|
throw new Error("Documentation response type did not match the requested projection.");
|
||||||
|
}
|
||||||
|
setContext(nextContext);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
if (sequence !== loadSequence.current) return;
|
||||||
setError(adminErrorMessage(err));
|
setError(adminErrorMessage(err));
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
if (sequence === loadSequence.current) setLoading(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user