feat(workbench): expand SCADA monitoring and investigation presentation
This commit is contained in:
@@ -5,6 +5,7 @@ import { env } from "@/shared/config/env";
|
||||
import type { NetworkViewParams } from "../map/camera";
|
||||
import { GEOSERVER_WORKSPACE } from "../map/geoserver-config";
|
||||
import { getWorkbenchAccessToken, getWorkbenchProject, type WorkbenchAccess } from "./request-context";
|
||||
import { PROJECT_REQUEST_TIMEOUT_MS, withProjectRequest } from "./project-request";
|
||||
|
||||
const projectSchema = z.object({
|
||||
project_id: z.string().min(1),
|
||||
@@ -117,11 +118,11 @@ export async function findOwnedWorkbenchProject(
|
||||
if (existing && existing.expiresAt > Date.now()) {
|
||||
return { project: await awaitProject(existing.promise, signal), accessToken };
|
||||
}
|
||||
const loadProject = async () => {
|
||||
const loadProject = () => withProjectRequest(SERVER_API_BASE_URL, "读取授权项目", async (requestSignal) => {
|
||||
const response = await fetch(`${SERVER_API_BASE_URL}/api/v1/projects?limit=1000&offset=0`, {
|
||||
headers: createWorkbenchHeaders(accessToken),
|
||||
// One caller cancelling must not cancel other consumers of this lookup.
|
||||
signal: getAccessToken ? AbortSignal.timeout(15_000) : signal
|
||||
signal: requestSignal
|
||||
});
|
||||
const payload: unknown = await response.json().catch(() => null);
|
||||
if (!response.ok) throw new Error(getServerError(payload, response.status, "项目查询"));
|
||||
@@ -131,7 +132,7 @@ export async function findOwnedWorkbenchProject(
|
||||
);
|
||||
if (!project) throw new Error(`未找到GeoServer工作空间 ${GEOSERVER_WORKSPACE} 对应的授权项目`);
|
||||
return project;
|
||||
};
|
||||
}, getAccessToken ? undefined : signal);
|
||||
const pending = loadProject();
|
||||
if (getAccessToken) {
|
||||
projectContextPromises.set(getAccessToken, { promise: pending, expiresAt: Date.now() + PROJECT_CACHE_TTL_MS });
|
||||
@@ -183,7 +184,7 @@ export async function fetchWorkbenchProjects(getAccessToken?: AccessTokenProvide
|
||||
if (!getAccessToken) return loadWorkbenchProjects(getAccessToken, signal);
|
||||
let cache = projectListCaches.get(getAccessToken);
|
||||
if (!cache) { cache = new ResourceCache(); projectListCaches.set(getAccessToken, cache); }
|
||||
const pending = cache.load("projects", () => loadWorkbenchProjects(getAccessToken, AbortSignal.timeout(15_000)),
|
||||
const pending = cache.load("projects", () => loadWorkbenchProjects(getAccessToken, AbortSignal.timeout(PROJECT_REQUEST_TIMEOUT_MS)),
|
||||
PROJECT_CACHE_TTL_MS, { force });
|
||||
if (!signal) return pending;
|
||||
signal.throwIfAborted();
|
||||
@@ -200,12 +201,14 @@ async function loadWorkbenchProjects(getAccessToken?: AccessTokenProvider, signa
|
||||
const projects: WorkbenchProject[] = [];
|
||||
let offset = 0;
|
||||
while (true) {
|
||||
const response = await fetch(`${SERVER_API_BASE_URL}/api/v1/projects?limit=1000&offset=${offset}`, {
|
||||
headers: createWorkbenchHeaders(token), signal
|
||||
});
|
||||
const payload: unknown = await response.json().catch(() => null);
|
||||
if (!response.ok) throw new Error(getServerError(payload, response.status, "项目查询"));
|
||||
const page = projectPageSchema.parse(payload);
|
||||
const page = await withProjectRequest(SERVER_API_BASE_URL, "读取授权项目", async (requestSignal) => {
|
||||
const response = await fetch(`${SERVER_API_BASE_URL}/api/v1/projects?limit=1000&offset=${offset}`, {
|
||||
headers: createWorkbenchHeaders(token), signal: requestSignal
|
||||
});
|
||||
const payload: unknown = await response.json().catch(() => null);
|
||||
if (!response.ok) throw new Error(getServerError(payload, response.status, "项目查询"));
|
||||
return projectPageSchema.parse(payload);
|
||||
}, signal);
|
||||
projects.push(...page.items.filter((project) => project.status === "active"));
|
||||
offset += page.items.length;
|
||||
if (offset >= page.total) break;
|
||||
@@ -236,19 +239,21 @@ async function fetchProjectMetadata(
|
||||
headers: Headers,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
const response = await fetch(`${SERVER_API_BASE_URL}/api/v1/projects/current/metadata`, {
|
||||
headers,
|
||||
signal
|
||||
});
|
||||
const payload: unknown = await response.json().catch(() => null);
|
||||
if (!response.ok) {
|
||||
throw new Error(getServerError(payload, response.status, "项目元数据查询"));
|
||||
}
|
||||
const metadata = projectMetadataSchema.parse(payload);
|
||||
if (metadata.project_id !== project.project_id) {
|
||||
throw new Error("项目元数据与授权项目不一致");
|
||||
}
|
||||
return metadata;
|
||||
return withProjectRequest(SERVER_API_BASE_URL, "读取项目配置", async (requestSignal) => {
|
||||
const response = await fetch(`${SERVER_API_BASE_URL}/api/v1/projects/current/metadata`, {
|
||||
headers,
|
||||
signal: requestSignal
|
||||
});
|
||||
const payload: unknown = await response.json().catch(() => null);
|
||||
if (!response.ok) {
|
||||
throw new Error(getServerError(payload, response.status, "项目元数据查询"));
|
||||
}
|
||||
const metadata = projectMetadataSchema.parse(payload);
|
||||
if (metadata.project_id !== project.project_id) {
|
||||
throw new Error("项目元数据与授权项目不一致");
|
||||
}
|
||||
return metadata;
|
||||
}, signal);
|
||||
}
|
||||
|
||||
async function fetchAccessContext(
|
||||
@@ -256,19 +261,21 @@ async function fetchAccessContext(
|
||||
headers: Headers,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
const response = await fetch(`${SERVER_API_BASE_URL}/api/v1/access-context`, {
|
||||
headers,
|
||||
signal
|
||||
});
|
||||
const payload: unknown = await response.json().catch(() => null);
|
||||
if (!response.ok) {
|
||||
throw new Error(getServerError(payload, response.status, "访问上下文查询"));
|
||||
}
|
||||
const accessContext = accessContextSchema.parse(payload);
|
||||
if (accessContext.project_id !== project.project_id) {
|
||||
throw new Error("访问上下文与授权项目不一致");
|
||||
}
|
||||
return accessContext;
|
||||
return withProjectRequest(SERVER_API_BASE_URL, "读取项目访问权限", async (requestSignal) => {
|
||||
const response = await fetch(`${SERVER_API_BASE_URL}/api/v1/access-context`, {
|
||||
headers,
|
||||
signal: requestSignal
|
||||
});
|
||||
const payload: unknown = await response.json().catch(() => null);
|
||||
if (!response.ok) {
|
||||
throw new Error(getServerError(payload, response.status, "访问上下文查询"));
|
||||
}
|
||||
const accessContext = accessContextSchema.parse(payload);
|
||||
if (accessContext.project_id !== project.project_id) {
|
||||
throw new Error("访问上下文与授权项目不一致");
|
||||
}
|
||||
return accessContext;
|
||||
}, signal);
|
||||
}
|
||||
|
||||
export function createWorkbenchHeaders(
|
||||
|
||||
Reference in New Issue
Block a user