mirror of
https://github.com/logos-messaging/libchat.git
synced 2026-03-27 14:43:06 +00:00
feat: run migrations from sql files
This commit is contained in:
parent
e099d5fd15
commit
f4c08bd048
@ -3,38 +3,10 @@
|
||||
use storage::{RusqliteError, SqliteDb, StorageConfig, StorageError, params};
|
||||
use x25519_dalek::StaticSecret;
|
||||
|
||||
use super::migrations;
|
||||
use super::types::{ChatRecord, IdentityRecord};
|
||||
use crate::identity::Identity;
|
||||
|
||||
/// Schema for chat storage tables.
|
||||
/// Note: Ratchet state is stored by double_ratchets::RatchetStorage separately.
|
||||
const CHAT_SCHEMA: &str = "
|
||||
-- Identity table (single row)
|
||||
CREATE TABLE IF NOT EXISTS identity (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
name TEXT NOT NULL,
|
||||
secret_key BLOB NOT NULL
|
||||
);
|
||||
|
||||
-- Inbox ephemeral keys for handshakes
|
||||
CREATE TABLE IF NOT EXISTS inbox_keys (
|
||||
public_key_hex TEXT PRIMARY KEY,
|
||||
secret_key BLOB NOT NULL,
|
||||
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||
);
|
||||
|
||||
-- Chat metadata
|
||||
CREATE TABLE IF NOT EXISTS chats (
|
||||
chat_id TEXT PRIMARY KEY,
|
||||
chat_type TEXT NOT NULL,
|
||||
remote_public_key BLOB,
|
||||
remote_address TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_chats_type ON chats(chat_type);
|
||||
";
|
||||
|
||||
/// Chat-specific storage operations.
|
||||
///
|
||||
/// This struct wraps a SqliteDb and provides domain-specific
|
||||
@ -49,12 +21,12 @@ impl ChatStorage {
|
||||
/// Creates a new ChatStorage with the given configuration.
|
||||
pub fn new(config: StorageConfig) -> Result<Self, StorageError> {
|
||||
let db = SqliteDb::new(config)?;
|
||||
Self::run_migration(db)
|
||||
Self::run_migrations(db)
|
||||
}
|
||||
|
||||
/// Creates a new chat storage with the given database.
|
||||
fn run_migration(db: SqliteDb) -> Result<Self, StorageError> {
|
||||
db.connection().execute_batch(CHAT_SCHEMA)?;
|
||||
/// Applies all migrations and returns the storage instance.
|
||||
fn run_migrations(db: SqliteDb) -> Result<Self, StorageError> {
|
||||
migrations::apply_migrations(db.connection())?;
|
||||
Ok(Self { db })
|
||||
}
|
||||
|
||||
|
||||
44
conversations/src/storage/migrations.rs
Normal file
44
conversations/src/storage/migrations.rs
Normal file
@ -0,0 +1,44 @@
|
||||
//! Database migrations module.
|
||||
//!
|
||||
//! SQL migrations are embedded at compile time and applied in order.
|
||||
|
||||
use storage::{Connection, StorageError};
|
||||
|
||||
/// Embeds and returns all migration SQL files in order.
|
||||
pub fn get_migrations() -> Vec<(&'static str, &'static str)> {
|
||||
vec![(
|
||||
"001_initial_schema",
|
||||
include_str!("migrations/001_initial_schema.sql"),
|
||||
)]
|
||||
}
|
||||
|
||||
/// Applies all migrations to the database.
|
||||
/// Uses a simple version tracking table to avoid re-running migrations.
|
||||
pub fn apply_migrations(conn: &Connection) -> Result<(), StorageError> {
|
||||
// Create migrations tracking table if it doesn't exist
|
||||
conn.execute_batch(
|
||||
"CREATE TABLE IF NOT EXISTS _migrations (
|
||||
name TEXT PRIMARY KEY,
|
||||
applied_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||
);",
|
||||
)?;
|
||||
|
||||
for (name, sql) in get_migrations() {
|
||||
// Check if migration already applied
|
||||
let already_applied: bool = conn.query_row(
|
||||
"SELECT EXISTS(SELECT 1 FROM _migrations WHERE name = ?1)",
|
||||
[name],
|
||||
|row| row.get(0),
|
||||
)?;
|
||||
|
||||
if !already_applied {
|
||||
// Apply migration
|
||||
conn.execute_batch(sql)?;
|
||||
|
||||
// Record migration
|
||||
conn.execute("INSERT INTO _migrations (name) VALUES (?1)", [name])?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
27
conversations/src/storage/migrations/001_initial_schema.sql
Normal file
27
conversations/src/storage/migrations/001_initial_schema.sql
Normal file
@ -0,0 +1,27 @@
|
||||
-- Initial schema for chat storage
|
||||
-- Migration: 001_initial_schema
|
||||
|
||||
-- Identity table (single row)
|
||||
CREATE TABLE IF NOT EXISTS identity (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
name TEXT NOT NULL,
|
||||
secret_key BLOB NOT NULL
|
||||
);
|
||||
|
||||
-- Inbox ephemeral keys for handshakes
|
||||
CREATE TABLE IF NOT EXISTS inbox_keys (
|
||||
public_key_hex TEXT PRIMARY KEY,
|
||||
secret_key BLOB NOT NULL,
|
||||
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||
);
|
||||
|
||||
-- Chat metadata
|
||||
CREATE TABLE IF NOT EXISTS chats (
|
||||
chat_id TEXT PRIMARY KEY,
|
||||
chat_type TEXT NOT NULL,
|
||||
remote_public_key BLOB,
|
||||
remote_address TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_chats_type ON chats(chat_type);
|
||||
@ -7,6 +7,7 @@
|
||||
//! handles all storage operations automatically.
|
||||
|
||||
mod db;
|
||||
mod migrations;
|
||||
pub(crate) mod types;
|
||||
|
||||
pub(crate) use db::ChatStorage;
|
||||
|
||||
@ -12,4 +12,4 @@ pub use errors::StorageError;
|
||||
pub use sqlite::{SqliteDb, StorageConfig};
|
||||
|
||||
// Re-export rusqlite types that domain crates will need
|
||||
pub use rusqlite::{Error as RusqliteError, Transaction, params};
|
||||
pub use rusqlite::{Connection, Error as RusqliteError, Transaction, params};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user