feat: schema version tracking, SHA256 integrity, setup scripts, bug fixes

This commit is contained in:
Accusys
2026-05-15 18:06:36 +08:00
parent 0e73d2a2ce
commit c41f7e0c6e
567 changed files with 55195 additions and 24 deletions
+29 -6
View File
@@ -2796,14 +2796,37 @@ impl PostgresDb {
uuid: &str,
) -> Result<Option<Chunk>> {
let table = schema::table_name("chunk");
let columns = "COALESCE(file_id, 0) as file_id, file_uuid, chunk_id, chunk_type, COALESCE(fps, 24.0) as fps, COALESCE(start_frame, 0) as start_frame, COALESCE(end_frame, 0) as end_frame, text_content, content, metadata, vector_id, COALESCE(frame_count, 0) as frame_count, pre_chunk_ids, parent_chunk_id, child_chunk_ids, visual_stats";
// Try exact chunk_id match first
let row = sqlx::query(&format!(
"SELECT COALESCE(file_id, 0) as file_id, file_uuid, chunk_id, chunk_type, COALESCE(fps, 24.0) as fps, COALESCE(start_frame, 0) as start_frame, COALESCE(end_frame, 0) as end_frame, text_content, content, metadata, vector_id, COALESCE(frame_count, 0) as frame_count, pre_chunk_ids, parent_chunk_id, child_chunk_ids, visual_stats FROM {} WHERE chunk_id = $1 AND file_uuid = $2",
table
"SELECT {} FROM {} WHERE chunk_id = $1 AND file_uuid = $2",
columns, table
))
.bind(chunk_id)
.bind(uuid)
.fetch_optional(&self.pool)
.await?;
.bind(chunk_id)
.bind(uuid)
.fetch_optional(&self.pool)
.await?;
// Fallback: if chunk_id is numeric (stale Qdrant payload), try matching by id
let row = if row.is_some() {
row
} else if chunk_id.bytes().all(|b| b.is_ascii_digit()) {
if let Ok(id) = chunk_id.parse::<i32>() {
sqlx::query(&format!(
"SELECT {} FROM {} WHERE id = $1 AND file_uuid = $2",
columns, table
))
.bind(id)
.bind(uuid)
.fetch_optional(&self.pool)
.await?
} else {
row
}
} else {
row
};
if let Some(r) = row {
let chunk_type_str: String = r.get(3);
+76
View File
@@ -1,5 +1,6 @@
use anyhow::{Context, Result};
use libc;
use std::collections::HashMap;
use std::path::PathBuf;
use std::process::Stdio;
use std::time::Duration;
@@ -45,6 +46,41 @@ impl RetryConfig {
}
}
/// Load SHA256 checksums from checksums.sha256 manifest.
/// Returns a map of relative path → expected hash.
fn load_checksums(scripts_dir: &PathBuf) -> HashMap<String, String> {
let path = scripts_dir.join("checksums.sha256");
let mut map = HashMap::new();
match std::fs::read_to_string(&path) {
Ok(content) => {
for line in content.lines() {
let line = line.trim();
if line.is_empty() {
continue;
}
if let Some((hash, rel_path)) = line.split_once(' ') {
let hash = hash.trim().to_string();
let rel_path = rel_path.trim().to_string();
if !hash.is_empty() && !rel_path.is_empty() {
map.insert(rel_path, hash);
}
}
}
tracing::info!(
"[CHECKSUMS] Loaded {} entries from checksums.sha256",
map.len()
);
}
Err(e) => {
tracing::warn!(
"[CHECKSUMS] Could not load checksums.sha256: {} — integrity checks disabled",
e
);
}
}
map
}
pub fn validate_python_env() -> Result<()> {
let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let venv_python = manifest.join("venv").join("bin").join("python");
@@ -85,6 +121,7 @@ pub fn validate_python_env() -> Result<()> {
pub struct PythonExecutor {
venv_python: PathBuf,
scripts_dir: PathBuf,
checksums: HashMap<String, String>,
}
impl PythonExecutor {
@@ -104,12 +141,47 @@ impl PythonExecutor {
anyhow::bail!("Scripts directory not found at {:?}", scripts_dir);
}
// Load SHA256 checksums manifest
let checksums = load_checksums(&scripts_dir);
Ok(Self {
venv_python,
scripts_dir,
checksums,
})
}
/// Verify a script's SHA256 against the checksums manifest before execution.
fn verify_script_integrity(&self, script_name: &str) -> Result<()> {
let script_path = self.scripts_dir.join(script_name);
let rel_path = format!("./{}", script_name);
if let Some(expected_hash) = self.checksums.get(&rel_path) {
let output = std::process::Command::new("shasum")
.arg("-a").arg("256")
.arg(&script_path)
.output()
.context("Failed to run shasum for integrity check")?;
let actual = String::from_utf8_lossy(&output.stdout);
let actual_hash = actual.split(' ').next().unwrap_or("").trim().to_string();
if actual_hash.is_empty() || actual_hash != *expected_hash {
anyhow::bail!(
"Script integrity check FAILED for {}: hash mismatch\n Expected: {}\n Actual: {}\n Run: bash scripts/setup/check_momentry.sh",
script_name, expected_hash, actual_hash
);
}
tracing::debug!("[INTEGRITY] {} checksum OK", script_name);
} else {
tracing::warn!(
"[INTEGRITY] {} not in checksums.sha256 manifest — skipping verification",
script_name
);
}
Ok(())
}
pub fn validate_env(&self) -> Result<()> {
let rt = tokio::runtime::Runtime::new()?;
let output = rt
@@ -147,6 +219,10 @@ impl PythonExecutor {
anyhow::bail!("Script not found: {:?}", script_path);
}
// Verify script integrity via SHA256 checksum before execution
self.verify_script_integrity(script_name)
.context("Pre-execution integrity check failed — possible version mismatch or corruption")?;
// 標記輸出檔為處理中(add .tmp suffix
let output_path = args.get(1).map(|p| std::path::PathBuf::from(p));
let tmp_path = output_path.as_ref().map(|p| {