feat: multi-library foundation (schema + libraries module)
Adds a `libraries` registry table and threads library_id through per-instance metadata tables (image_exif, photo_insights, entity_photo_links, video_preview_clips). File-path columns renamed to rel_path to make the relative-to-root semantics explicit. Adds content_hash + size_bytes on image_exif to support future hash-keyed thumbnail/HLS dedup. Tags and favorites stay library-agnostic so they share across libraries by rel_path. Behavior is unchanged: a single primary library (id=1) is seeded from BASE_PATH on first boot; all handlers and DAOs route through it as a transitional shim until the API gains a library query param. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -550,8 +550,8 @@ impl KnowledgeDao for SqliteKnowledgeDao {
|
||||
|
||||
// 3. Copy photo links to target (INSERT OR IGNORE to skip duplicates)
|
||||
let links_updated = diesel::sql_query(
|
||||
"INSERT OR IGNORE INTO entity_photo_links (entity_id, file_path, role) \
|
||||
SELECT ?, file_path, role FROM entity_photo_links WHERE entity_id = ?",
|
||||
"INSERT OR IGNORE INTO entity_photo_links (entity_id, library_id, rel_path, role) \
|
||||
SELECT ?, library_id, rel_path, role FROM entity_photo_links WHERE entity_id = ?",
|
||||
)
|
||||
.bind::<diesel::sql_types::Integer, _>(target_id)
|
||||
.bind::<diesel::sql_types::Integer, _>(source_id)
|
||||
@@ -781,11 +781,12 @@ impl KnowledgeDao for SqliteKnowledgeDao {
|
||||
) -> Result<(), DbError> {
|
||||
trace_db_call(cx, "insert", "upsert_photo_link", |_span| {
|
||||
let mut conn = self.connection.lock().expect("KnowledgeDao lock");
|
||||
// INSERT OR IGNORE respects the UNIQUE(entity_id, file_path, role) constraint
|
||||
// INSERT OR IGNORE respects the UNIQUE(entity_id, library_id, rel_path, role) constraint
|
||||
diesel::sql_query(
|
||||
"INSERT OR IGNORE INTO entity_photo_links (entity_id, file_path, role) VALUES (?, ?, ?)"
|
||||
"INSERT OR IGNORE INTO entity_photo_links (entity_id, library_id, rel_path, role) VALUES (?, ?, ?, ?)"
|
||||
)
|
||||
.bind::<diesel::sql_types::Integer, _>(link.entity_id)
|
||||
.bind::<diesel::sql_types::Integer, _>(link.library_id)
|
||||
.bind::<diesel::sql_types::Text, _>(&link.file_path)
|
||||
.bind::<diesel::sql_types::Text, _>(&link.role)
|
||||
.execute(conn.deref_mut())
|
||||
@@ -803,7 +804,7 @@ impl KnowledgeDao for SqliteKnowledgeDao {
|
||||
trace_db_call(cx, "delete", "delete_photo_links_for_file", |_span| {
|
||||
use schema::entity_photo_links::dsl::*;
|
||||
let mut conn = self.connection.lock().expect("KnowledgeDao lock");
|
||||
diesel::delete(entity_photo_links.filter(file_path.eq(file_path_val)))
|
||||
diesel::delete(entity_photo_links.filter(rel_path.eq(file_path_val)))
|
||||
.execute(conn.deref_mut())
|
||||
.map(|_| ())
|
||||
.map_err(|e| anyhow::anyhow!("Delete error: {}", e))
|
||||
@@ -820,7 +821,7 @@ impl KnowledgeDao for SqliteKnowledgeDao {
|
||||
use schema::entity_photo_links::dsl::*;
|
||||
let mut conn = self.connection.lock().expect("KnowledgeDao lock");
|
||||
entity_photo_links
|
||||
.filter(file_path.eq(file_path_val))
|
||||
.filter(rel_path.eq(file_path_val))
|
||||
.load::<EntityPhotoLink>(conn.deref_mut())
|
||||
.map_err(|e| anyhow::anyhow!("Query error: {}", e))
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user