20260718 重构 RPKI Explorer 查询界面

This commit is contained in:
2026-07-18 10:38:38 +08:00
parent a77982deaa
commit a5902c0a5e
79 changed files with 7964 additions and 4861 deletions

View File

@@ -50,6 +50,68 @@ pub enum ObjectLookup {
Sha256(String),
}
/// Optional predicates applied while streaming report objects. `reason` and
/// `query` must be pre-lowercased by the caller; they are matched as
/// case-insensitive substrings. The other fields are exact matches.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ObjectFilter {
pub object_type: Option<String>,
pub result: Option<String>,
pub rejected: Option<bool>,
pub reason: Option<String>,
pub query: Option<String>,
pub repo_id: Option<String>,
pub pp_id: Option<String>,
pub issues_only: bool,
}
impl ObjectFilter {
pub fn matches(&self, object: &ObjectInstanceRecord) -> bool {
if self.issues_only && !(object.rejected || object.result == "error") {
return false;
}
if let Some(rejected) = self.rejected
&& object.rejected != rejected
{
return false;
}
if let Some(result) = self.result.as_deref()
&& !object.result.eq_ignore_ascii_case(result)
{
return false;
}
if let Some(object_type) = self.object_type.as_deref()
&& !object.object_type.eq_ignore_ascii_case(object_type)
{
return false;
}
if let Some(repo_id) = self.repo_id.as_deref()
&& object.repo_id != repo_id
{
return false;
}
if let Some(pp_id) = self.pp_id.as_deref()
&& object.pp_id != pp_id
{
return false;
}
if let Some(reason) = self.reason.as_deref() {
let Some(reject_reason) = object.reject_reason.as_deref() else {
return false;
};
if !reject_reason.to_lowercase().contains(reason) {
return false;
}
}
if let Some(query) = self.query.as_deref()
&& !object.uri.to_lowercase().contains(query)
{
return false;
}
true
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ObjectResolutionMeta {
@@ -81,13 +143,37 @@ pub fn list_report_objects(
scope: ObjectScope,
limit: usize,
cursor: Option<&str>,
) -> QueryDbResult<QueryPage<ObjectInstanceRecord>> {
list_report_objects_filtered(
report_path,
run_id,
scope,
&ObjectFilter::default(),
limit,
cursor,
)
}
pub fn list_report_objects_filtered(
report_path: &Path,
run_id: &str,
scope: ObjectScope,
filter: &ObjectFilter,
limit: usize,
cursor: Option<&str>,
) -> QueryDbResult<QueryPage<ObjectInstanceRecord>> {
let (start_pp_index, start_object_index) = parse_object_cursor(cursor)?;
let file = File::open(report_path)?;
let reader = BufReader::new(file);
let mut deserializer = serde_json::Deserializer::from_reader(reader);
let mut state =
ObjectScanState::new_list(run_id, scope, limit, start_pp_index, start_object_index);
let mut state = ObjectScanState::new_list(
run_id,
scope,
filter.clone(),
limit,
start_pp_index,
start_object_index,
);
ObjectScanSeed { state: &mut state }
.deserialize(&mut deserializer)
.map_err(QueryDbError::from)?;
@@ -530,6 +616,7 @@ struct ObjectScanState<'a> {
run_id: &'a str,
scope: ObjectScope,
mode: ObjectScanMode,
filter: ObjectFilter,
limit: usize,
start_pp_index: u64,
start_object_index: u64,
@@ -544,6 +631,7 @@ impl<'a> ObjectScanState<'a> {
fn new_list(
run_id: &'a str,
scope: ObjectScope,
filter: ObjectFilter,
limit: usize,
start_pp_index: u64,
start_object_index: u64,
@@ -552,6 +640,7 @@ impl<'a> ObjectScanState<'a> {
run_id,
scope,
mode: ObjectScanMode::List,
filter,
limit,
start_pp_index,
start_object_index,
@@ -568,6 +657,7 @@ impl<'a> ObjectScanState<'a> {
run_id,
scope,
mode: ObjectScanMode::Lookup(lookup),
filter: ObjectFilter::default(),
limit: 1,
start_pp_index: 0,
start_object_index: 0,
@@ -682,7 +772,8 @@ fn process_raw_publication_point(
let after_cursor = pp_index > state.start_pp_index
|| (pp_index == state.start_pp_index
&& object_index >= state.start_object_index);
if after_cursor && state.data.len() < state.limit {
if after_cursor && state.data.len() < state.limit && state.filter.matches(&record)
{
state.data.push(record);
if state.data.len() == state.limit {
state.next_cursor = Some(object_cursor(pp_index, object_index + 1));