mod sqlite;
use anyhow::Result;
use chrono::Datelike;
use mr_common::Memory;
use std::sync::Arc;
pub use sqlite::SqliteArchive;
#[async_trait::async_trait]
pub trait ArchiveStorage: Send + Sync {
async fn archive(&self, memory: &Memory) -> Result<()>;
async fn query_range(
&self,
year: i32,
limit: usize,
offset: usize,
) -> Result<Vec<ArchivedMemory>>;
async fn count(&self, year: i32) -> Result<usize>;
}
#[derive(Debug, Clone)]
pub struct ArchivedMemory {
pub id: String,
pub content: String,
pub memory_type: String,
pub tags: Vec<String>,
pub project_id: Option<String>,
pub is_global: bool,
pub created_at: String,
pub source: String,
pub metadata: serde_json::Value,
}
pub struct ArchiveManager {
data_dir: std::path::PathBuf,
enabled: bool,
}
impl ArchiveManager {
pub fn new(data_dir: std::path::PathBuf, enabled: bool) -> Self {
Self { data_dir, enabled }
}
fn get_archive_path(&self, year: i32) -> std::path::PathBuf {
self.data_dir
.join("archive")
.join(format!("raw_{}.db", year))
}
pub fn get_current_archive(&self) -> Option<Arc<SqliteArchive>> {
if !self.enabled {
return None;
}
let year = chrono::Utc::now().year();
let path = self.get_archive_path(year);
if let Some(parent) = path.parent() {
let _ = std::fs::create_dir_all(parent);
}
match SqliteArchive::open(&path) {
Ok(archive) => Some(Arc::new(archive)),
Err(e) => {
tracing::warn!("Failed to open archive {}: {}", path.display(), e);
None
}
}
}
pub async fn archive(&self, memory: &Memory) {
if !self.enabled {
return;
}
let year = memory.created_at.year();
let path = self.get_archive_path(year);
if let Some(parent) = path.parent() {
let _ = std::fs::create_dir_all(parent);
}
match SqliteArchive::open(&path) {
Ok(archive) => {
if let Err(e) = archive.archive_sync(memory) {
tracing::warn!("Failed to archive memory {}: {}", memory.id, e);
}
}
Err(e) => {
tracing::warn!("Failed to open archive for year {}: {}", year, e);
}
}
}
pub fn get_archive(&self, year: i32) -> Result<Option<Arc<SqliteArchive>>> {
if !self.enabled {
return Ok(None);
}
let path = self.get_archive_path(year);
if !path.exists() {
return Ok(None);
}
let archive = SqliteArchive::open(&path)?;
Ok(Some(Arc::new(archive)))
}
}