20260718 重构 RPKI Explorer 查询界面
This commit is contained in:
308
ui/rpki-explorer/src/api/schemas.ts
Normal file
308
ui/rpki-explorer/src/api/schemas.ts
Normal file
@@ -0,0 +1,308 @@
|
||||
/**
|
||||
* Zod schemas describing the rpki_query_service API contract.
|
||||
*
|
||||
* Schemas are intentionally `.passthrough()` so additive backend changes do
|
||||
* not break the UI; fields the UI depends on are typed strictly.
|
||||
*/
|
||||
import { z } from "zod";
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Records */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
export const runCountsSchema = z
|
||||
.object({
|
||||
publicationPoints: z.number().optional(),
|
||||
objects: z.number().optional(),
|
||||
freshObjects: z.number().optional(),
|
||||
cachedObjects: z.number().optional(),
|
||||
rejectedObjects: z.number().optional(),
|
||||
freshRejectedObjects: z.number().optional(),
|
||||
cachedRejectedObjects: z.number().optional(),
|
||||
trustAnchors: z.number().optional(),
|
||||
vrps: z.number().optional(),
|
||||
aspas: z.number().optional(),
|
||||
warnings: z.number().optional(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const runRecordSchema = z
|
||||
.object({
|
||||
schemaVersion: z.number().optional(),
|
||||
runId: z.string(),
|
||||
runSeq: z.number().nullish(),
|
||||
runDir: z.string().optional(),
|
||||
validationTime: z.string().nullish(),
|
||||
syncMode: z.string().nullish(),
|
||||
startedAt: z.string().nullish(),
|
||||
finishedAt: z.string().nullish(),
|
||||
wallMs: z.number().nullish(),
|
||||
maxRssKb: z.number().nullish(),
|
||||
artifactPaths: z.record(z.string()).optional(),
|
||||
counts: runCountsSchema.optional(),
|
||||
indexStatus: z.string().optional(),
|
||||
indexError: z.string().nullish(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const repositoryRecordSchema = z
|
||||
.object({
|
||||
repoId: z.string(),
|
||||
uri: z.string(),
|
||||
host: z.string(),
|
||||
transport: z.string().nullish(),
|
||||
publicationPoints: z.number().nullish(),
|
||||
objects: z.number().nullish(),
|
||||
rejectedObjects: z.number().nullish(),
|
||||
downloadBytes: z.number().nullish(),
|
||||
syncDurationMsTotal: z.number().nullish(),
|
||||
phases: z.record(z.number()).optional(),
|
||||
terminalStates: z.record(z.number()).optional(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const publicationPointRecordSchema = z
|
||||
.object({
|
||||
ppId: z.string(),
|
||||
repoId: z.string(),
|
||||
nodeId: z.number().nullish(),
|
||||
parentNodeId: z.number().nullish(),
|
||||
rsyncBaseUri: z.string().nullish(),
|
||||
manifestRsyncUri: z.string().nullish(),
|
||||
publicationPointRsyncUri: z.string().nullish(),
|
||||
rrdpNotificationUri: z.string().nullish(),
|
||||
source: z.string().nullish(),
|
||||
repoSyncSource: z.string().nullish(),
|
||||
repoSyncPhase: z.string().nullish(),
|
||||
repoSyncDurationMs: z.number().nullish(),
|
||||
repoSyncError: z.string().nullish(),
|
||||
repoTerminalState: z.string().nullish(),
|
||||
thisUpdate: z.string().nullish(),
|
||||
nextUpdate: z.string().nullish(),
|
||||
verifiedAt: z.string().nullish(),
|
||||
objects: z.number().nullish(),
|
||||
rejectedObjects: z.number().nullish(),
|
||||
warnings: z.number().nullish(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const objectInstanceRecordSchema = z
|
||||
.object({
|
||||
objectInstanceId: z.string(),
|
||||
uri: z.string(),
|
||||
uriHash: z.string().optional(),
|
||||
sha256: z.string().optional(),
|
||||
objectType: z.string(),
|
||||
result: z.string().nullish(),
|
||||
detailSummary: z.string().nullish(),
|
||||
repoId: z.string(),
|
||||
ppId: z.string(),
|
||||
sourceSection: z.string().nullish(),
|
||||
rejected: z.boolean().optional(),
|
||||
rejectReason: z.string().nullish(),
|
||||
sizeBytes: z.number().nullish(),
|
||||
rawAvailable: z.boolean().optional(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const chainEdgeRecordSchema = z
|
||||
.object({
|
||||
relation: z.string(),
|
||||
fromUri: z.string().nullish(),
|
||||
toUri: z.string().nullish(),
|
||||
toObjectInstanceId: z.string().nullish(),
|
||||
toSha256: z.string().nullish(),
|
||||
status: z.string().nullish(),
|
||||
evidence: z.unknown().optional(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
/** Sparse record returned by /objects/by-uri (index hit, no lazy scan). */
|
||||
export const objectUriIndexRecordSchema = z
|
||||
.object({
|
||||
uri: z.string(),
|
||||
sha256: z.string(),
|
||||
objectInstanceId: z.string(),
|
||||
repoId: z.string(),
|
||||
ppId: z.string(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const exportJobRecordSchema = z
|
||||
.object({
|
||||
jobId: z.string(),
|
||||
runId: z.string(),
|
||||
scope: z.string(),
|
||||
repoId: z.string().nullish(),
|
||||
ppId: z.string().nullish(),
|
||||
status: z.string(),
|
||||
createdAt: z.string().nullish(),
|
||||
finishedAt: z.string().nullish(),
|
||||
outputPath: z.string().nullish(),
|
||||
objectCount: z.number().nullish(),
|
||||
bytesWritten: z.number().nullish(),
|
||||
error: z.string().nullish(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const projectionRecordSchema = z
|
||||
.object({
|
||||
schemaVersion: z.number().optional(),
|
||||
sha256: z.string().optional(),
|
||||
objectType: z.string().optional(),
|
||||
parseStatus: z.string().optional(),
|
||||
errorSummary: z.string().nullish(),
|
||||
projection: z.record(z.unknown()).nullish(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const validationIssueSchema = z
|
||||
.object({
|
||||
severity: z.string().nullish(),
|
||||
reasonCode: z.string().nullish(),
|
||||
reasonText: z.string().nullish(),
|
||||
rfcRefs: z.array(z.string()).optional(),
|
||||
evidence: z.unknown().optional(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const validationStageSchema = z
|
||||
.object({
|
||||
status: z.string().nullish(),
|
||||
stage: z.string().nullish(),
|
||||
mode: z.string().nullish(),
|
||||
projectionStatus: z.string().nullish(),
|
||||
issues: z.array(validationIssueSchema).optional(),
|
||||
detailSummary: z.string().nullish(),
|
||||
edgesCount: z.number().nullish(),
|
||||
note: z.string().nullish(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const validationSummarySchema = z
|
||||
.object({
|
||||
finalStatus: z.string().nullish(),
|
||||
auditResult: z.string().nullish(),
|
||||
detailSummary: z.string().nullish(),
|
||||
parsevalidate: validationStageSchema.nullish(),
|
||||
chainvalidate: validationStageSchema.nullish(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const explainRecordSchema = z
|
||||
.object({
|
||||
finalStatus: z.string().nullish(),
|
||||
auditResult: z.string().nullish(),
|
||||
authoritative: z.boolean().optional(),
|
||||
explainMode: z.string().nullish(),
|
||||
parsevalidate: validationStageSchema.nullish(),
|
||||
chainvalidate: validationStageSchema.nullish(),
|
||||
chainEdges: z.array(chainEdgeRecordSchema).optional(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const searchResultSchema = z
|
||||
.object({
|
||||
objects: z.array(objectInstanceRecordSchema),
|
||||
repos: z.array(repositoryRecordSchema),
|
||||
publicationPoints: z.array(publicationPointRecordSchema),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const healthSchema = z
|
||||
.object({
|
||||
status: z.string(),
|
||||
service: z.string().optional(),
|
||||
version: z.number().optional(),
|
||||
latestReadyRun: z.string().nullish(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const serviceInfoSchema = z
|
||||
.object({
|
||||
service: z.string(),
|
||||
version: z.number(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const manifestFileEntrySchema = z
|
||||
.object({
|
||||
fileName: z.string(),
|
||||
hashHex: z.string(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const revokedCertEntrySchema = z
|
||||
.object({
|
||||
serialNumberHex: z.string().nullish(),
|
||||
serialNumber: z.string().nullish(),
|
||||
revocationDate: z.string().nullish(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const runSummarySchema = z
|
||||
.object({
|
||||
publicationPoints: z.number().optional(),
|
||||
objects: z.number().optional(),
|
||||
repos: z.number().optional(),
|
||||
vrps: z.number().optional(),
|
||||
aspas: z.number().optional(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const statsMapSchema = z.record(z.number());
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Envelope */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
const pageSchema = z.object({
|
||||
nextCursor: z.string().nullable(),
|
||||
limit: z.number(),
|
||||
});
|
||||
|
||||
const metaSchema = z
|
||||
.object({
|
||||
runId: z.string().nullish(),
|
||||
schemaVersion: z.number().optional(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export function envelopeSchema<T extends z.ZodTypeAny>(dataSchema: T) {
|
||||
return z
|
||||
.object({
|
||||
data: dataSchema,
|
||||
page: pageSchema.nullish(),
|
||||
meta: metaSchema.optional(),
|
||||
})
|
||||
.passthrough();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Inferred types */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
export type RunCounts = z.infer<typeof runCountsSchema>;
|
||||
export type RunRecord = z.infer<typeof runRecordSchema>;
|
||||
export type RepositoryRecord = z.infer<typeof repositoryRecordSchema>;
|
||||
export type PublicationPointRecord = z.infer<typeof publicationPointRecordSchema>;
|
||||
export type ObjectInstanceRecord = z.infer<typeof objectInstanceRecordSchema>;
|
||||
export type ChainEdgeRecord = z.infer<typeof chainEdgeRecordSchema>;
|
||||
export type ExportJobRecord = z.infer<typeof exportJobRecordSchema>;
|
||||
export type ProjectionRecord = z.infer<typeof projectionRecordSchema>;
|
||||
export type ValidationIssue = z.infer<typeof validationIssueSchema>;
|
||||
export type ValidationStage = z.infer<typeof validationStageSchema>;
|
||||
export type ValidationSummary = z.infer<typeof validationSummarySchema>;
|
||||
export type ExplainRecord = z.infer<typeof explainRecordSchema>;
|
||||
export type SearchResult = z.infer<typeof searchResultSchema>;
|
||||
export type HealthStatus = z.infer<typeof healthSchema>;
|
||||
export type ServiceInfo = z.infer<typeof serviceInfoSchema>;
|
||||
export type ManifestFileEntry = z.infer<typeof manifestFileEntrySchema>;
|
||||
export type RevokedCertEntry = z.infer<typeof revokedCertEntrySchema>;
|
||||
export type RunSummary = z.infer<typeof runSummarySchema>;
|
||||
|
||||
export interface ListResult<T> {
|
||||
items: T[];
|
||||
nextCursor: string | null;
|
||||
runId: string | undefined;
|
||||
}
|
||||
Reference in New Issue
Block a user