wraith/src-tauri/src/db/mod.rs
Vantz Stockwell 2848d79915 feat: Phase 1 complete — Tauri v2 foundation
Rust backend: SQLite (WAL mode, 8 tables), vault encryption
(Argon2id + AES-256-GCM), settings/connections/credentials
services, 19 Tauri command wrappers. 46/46 tests passing.

Vue 3 frontend: unlock/create vault flow, Pinia app store,
Tailwind CSS v4 dark theme with Wraith branding.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 15:09:41 -04:00

50 lines
1.6 KiB
Rust

use rusqlite::Connection;
use std::path::Path;
use std::sync::{Arc, Mutex};
/// Cheap-to-clone handle to a single SQLite connection protected by a mutex.
///
/// Using a single shared connection (rather than a pool) is correct for
/// desktop use: SQLite's WAL mode allows concurrent reads from the OS level,
/// and the mutex ensures we never issue overlapping writes from Tauri's
/// async command threads.
#[derive(Clone)]
pub struct Database {
conn: Arc<Mutex<Connection>>,
}
impl Database {
/// Open (or create) the SQLite database at `path` and apply PRAGMAs.
pub fn open(path: &Path) -> Result<Self, Box<dyn std::error::Error>> {
let conn = Connection::open(path)?;
conn.execute_batch(
"PRAGMA journal_mode=WAL;
PRAGMA busy_timeout=5000;
PRAGMA foreign_keys=ON;",
)?;
Ok(Self {
conn: Arc::new(Mutex::new(conn)),
})
}
/// Acquire a lock on the underlying connection.
///
/// Panics if the mutex was poisoned (which only happens if a thread
/// panicked while holding the lock — a non-recoverable situation anyway).
pub fn conn(&self) -> std::sync::MutexGuard<'_, Connection> {
self.conn.lock().unwrap()
}
/// Run all embedded SQL migrations.
///
/// The migration file is compiled into the binary via `include_str!`,
/// so there is no runtime file-system dependency on it.
pub fn migrate(&self) -> Result<(), Box<dyn std::error::Error>> {
let conn = self.conn();
conn.execute_batch(include_str!("migrations/001_initial.sql"))?;
Ok(())
}
}