Files
nx9-url-shortener/src/state.rs
T
thakares f49698bb5c
Rust CI / Test & Quality Checks (push) Canceled after 0s
Rust CI / Build Docker Image (push) Canceled after 0s
Release v0.6.0
2026-08-09 17:17:57 +05:30

99 lines
3.5 KiB
Rust

use crate::analytics::queue::AnalyticsQueue;
use crate::config::Config;
use crate::db::Db;
use rusqlite::Connection;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::Instant;
#[derive(Clone)]
pub struct UserDbs {
pub content: Arc<Mutex<Connection>>,
pub analytics: Arc<Mutex<Connection>>,
pub profile: Arc<Mutex<Connection>>,
}
#[derive(Clone)]
pub struct AppState {
pub admin_db: Arc<Mutex<Connection>>,
pub content_db: Arc<Mutex<Connection>>,
pub analytics_db: Arc<Mutex<Connection>>,
pub system_db: Arc<Mutex<Connection>>,
pub users_db: Arc<Mutex<Connection>>,
pub user_dbs: Arc<Mutex<HashMap<i64, UserDbs>>>,
pub db: Db,
pub config: Config,
pub analytics_queue: AnalyticsQueue,
pub start_time: Instant,
}
impl AppState {
pub fn get_user_dbs(&self, user_id: i64) -> Result<UserDbs, crate::error::AppError> {
let mut pool = crate::utils::lock_db(&self.user_dbs, "user_dbs")?;
if let Some(dbs) = pool.get(&user_id) {
return Ok(dbs.clone());
}
// Open connection and run migrations
let user_dir = self.config.data_dir.join("users").join(user_id.to_string());
std::fs::create_dir_all(&user_dir)?;
let content_path = user_dir.join("content.db");
let analytics_path = user_dir.join("analytics.db");
let profile_path = user_dir.join("profile.db");
let mut content_conn = Connection::open(content_path)?;
let mut analytics_conn = Connection::open(analytics_path)?;
let profile_conn = Connection::open(profile_path)?;
crate::db::sqlite::enable_wal(&content_conn, "content")?;
crate::db::sqlite::enable_wal(&analytics_conn, "analytics")?;
crate::db::sqlite::enable_wal(&profile_conn, "profile")?;
crate::db::sqlite::enable_foreign_keys(&content_conn, "content")?;
crate::db::sqlite::enable_foreign_keys(&analytics_conn, "analytics")?;
crate::db::sqlite::enable_foreign_keys(&profile_conn, "profile")?;
// Run migrations
crate::db::migrations::run_migrations(
&mut content_conn,
"content",
crate::db::migrations::CONTENT_MIGRATIONS,
Some(&self.system_db),
)
.map_err(|e| crate::error::AppError::Internal(e.to_string()))?;
crate::db::migrations::run_migrations(
&mut analytics_conn,
"analytics",
crate::db::migrations::ANALYTICS_MIGRATIONS,
Some(&self.system_db),
)
.map_err(|e| crate::error::AppError::Internal(e.to_string()))?;
profile_conn.execute_batch(
"CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);",
)?;
let dbs = UserDbs {
content: Arc::new(Mutex::new(content_conn)),
analytics: Arc::new(Mutex::new(analytics_conn)),
profile: Arc::new(Mutex::new(profile_conn)),
};
pool.insert(user_id, dbs.clone());
Ok(dbs)
}
pub fn db_compact(&self) -> Result<(), crate::error::AppError> {
crate::utils::lock_db(&self.admin_db, "admin_db")?.execute("VACUUM;", [])?;
crate::utils::lock_db(&self.content_db, "content_db")?.execute("VACUUM;", [])?;
crate::utils::lock_db(&self.analytics_db, "analytics_db")?.execute("VACUUM;", [])?;
crate::utils::lock_db(&self.system_db, "system_db")?.execute("VACUUM;", [])?;
crate::utils::lock_db(&self.users_db, "users_db")?.execute("VACUUM;", [])?;
Ok(())
}
}