use anyhow::Result;
use sqlx::{Pool, Sqlite, SqlitePool};
use tracing::{debug, info};
use crate::config::StorageConfig;
#[derive(Clone)]
pub struct StorageService {
pool: Pool<Sqlite>,
}
#[derive(Debug, sqlx::FromRow, serde::Serialize)]
pub struct FileMetadata {
pub id: i64,
pub path: String,
pub name: String,
pub size: i64,
pub mime_type: String,
pub checksum: String,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
pub tags: Option<String>,
pub description: Option<String>,
}
#[derive(Debug, sqlx::FromRow)]
pub struct FileSearchData {
pub path: String,
pub content: String,
pub embeddings: Option<Vec<u8>>,
}
impl StorageService {
pub async fn new(config: &StorageConfig) -> Result<Self> {
info!("Connecting to database: {}", config.database_url);
let pool = SqlitePool::connect(&config.database_url).await?;
sqlx::migrate!("./migrations").run(&pool).await?;
Ok(Self { pool })
}
pub async fn store_file_metadata(&self, metadata: &FileMetadata) -> Result<i64> {
debug!("Storing file metadata: {}", metadata.path);
let result = sqlx::query!(
r#"
INSERT INTO file_metadata (path, name, size, mime_type, checksum, tags, description)
VALUES (?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(path) DO UPDATE SET
name = excluded.name,
size = excluded.size,
mime_type = excluded.mime_type,
checksum = excluded.checksum,
tags = excluded.tags,
description = excluded.description,
updated_at = CURRENT_TIMESTAMP
"#,
metadata.path,
metadata.name,
metadata.size,
metadata.mime_type,
metadata.checksum,
metadata.tags,
metadata.description
)
.execute(&self.pool)
.await?;
Ok(result.last_insert_rowid())
}
pub async fn get_file_metadata(&self, path: &str) -> Result<Option<FileMetadata>> {
debug!("Getting file metadata: {}", path);