fix: validate gps-summary path against every library

The /photos/gps-summary handler validated the incoming path against
the primary library's root with new_file=false, which requires the
path to exist on disk. For a viewer opened on a file from a
non-primary library, tapping the GPS link produced activePath =
<folder from lib 2>, the primary-only check failed, and the server
400'd — so the map came up empty.

Validation here is purely a traversal guard (the DAO does a prefix
LIKE against rel_path), so we now accept the path as long as any
configured library can resolve it without escaping its root.

Also applies cargo fmt drift on files touched this session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Cameron
2026-04-18 16:38:28 -04:00
committed by cameron
parent 54a1df60b8
commit a0f3bfab5f
4 changed files with 55 additions and 59 deletions

View File

@@ -1902,14 +1902,10 @@ Return ONLY the summary, nothing else."#,
// those already). Results are appended to the tool response so the
// model can choose to use an existing entity's ID instead.
let similar_entities: Vec<String> = {
use crate::database::{EntityFilter, KnowledgeDao};
use crate::database::knowledge_dao::normalize_entity_type;
use crate::database::{EntityFilter, KnowledgeDao};
let normalised_type = normalize_entity_type(&entity_type);
let first_token = name
.split_whitespace()
.next()
.unwrap_or(&name)
.to_string();
let first_token = name.split_whitespace().next().unwrap_or(&name).to_string();
let filter = EntityFilter {
entity_type: None, // search all types, filter client-side to avoid case issues
status: Some("active".to_string()),
@@ -1917,7 +1913,10 @@ Return ONLY the summary, nothing else."#,
limit: 10,
offset: 0,
};
let mut kdao = self.knowledge_dao.lock().expect("Unable to lock KnowledgeDao");
let mut kdao = self
.knowledge_dao
.lock()
.expect("Unable to lock KnowledgeDao");
kdao.list_entities(cx, filter)
.unwrap_or_default()
.0
@@ -2725,10 +2724,9 @@ Return ONLY the summary, nothing else."#,
messages.push(ChatMessage::user(
"Based on the context gathered, please write the final photo insight: a title and a detailed personal summary. Write in first person as Cameron.",
));
let (final_response, prompt_tokens, eval_tokens) =
ollama_client
.chat_with_tools(messages.clone(), vec![])
.await?;
let (final_response, prompt_tokens, eval_tokens) = ollama_client
.chat_with_tools(messages.clone(), vec![])
.await?;
last_prompt_eval_count = prompt_tokens;
last_eval_count = eval_tokens;
final_content = final_response.content.clone();