Skip to content

Commit

Permalink
WIP: Getting close to SQLite working
Browse files Browse the repository at this point in the history
  • Loading branch information
erskingardner committed Jan 23, 2025
1 parent 2e61bf3 commit 4c0061a
Show file tree
Hide file tree
Showing 16 changed files with 273 additions and 179 deletions.
71 changes: 44 additions & 27 deletions src-tauri/db_migrations/0001_initial.sql
Original file line number Diff line number Diff line change
Expand Up @@ -23,32 +23,52 @@ BEGIN
UPDATE accounts SET active = FALSE WHERE active = TRUE AND pubkey != NEW.pubkey;
END;

-- Relays table with type and ownership
CREATE TABLE relays (
-- Account-specific relays table
CREATE TABLE account_relays (
id INTEGER PRIMARY KEY AUTOINCREMENT,
url TEXT NOT NULL,
relay_type TEXT NOT NULL,
account_pubkey TEXT NOT NULL,
group_id BLOB,
FOREIGN KEY (account_pubkey) REFERENCES accounts(pubkey) ON DELETE CASCADE,
PRIMARY KEY (url, relay_type, account_pubkey, group_id)
FOREIGN KEY (account_pubkey) REFERENCES accounts(pubkey) ON DELETE CASCADE
);

CREATE INDEX idx_account_relays_account ON account_relays(account_pubkey, relay_type);

-- Group-specific relays table (separate table)
CREATE TABLE group_relays (
id INTEGER PRIMARY KEY AUTOINCREMENT,
url TEXT NOT NULL,
relay_type TEXT NOT NULL,
account_pubkey TEXT NOT NULL,
group_id BLOB NOT NULL,
FOREIGN KEY (group_id, account_pubkey) REFERENCES groups(mls_group_id, account_pubkey) ON DELETE CASCADE
);

CREATE INDEX idx_group_relays_group ON group_relays(group_id, relay_type);
CREATE INDEX idx_group_relays_group_account ON group_relays(group_id, account_pubkey);

-- Groups table matching the Group struct
CREATE TABLE groups (
mls_group_id BLOB PRIMARY KEY,
mls_group_id BLOB,
account_pubkey TEXT NOT NULL,
nostr_group_id TEXT NOT NULL,
name TEXT NOT NULL,
description TEXT NOT NULL,
name TEXT,
description TEXT,
admin_pubkeys TEXT NOT NULL, -- JSON array of strings
last_message_id TEXT,
last_message_at INTEGER,
group_type TEXT NOT NULL CHECK (group_type IN ('DirectMessage', 'Group')),
epoch INTEGER NOT NULL,
state TEXT NOT NULL CHECK (state IN ('Active', 'Inactive')),
FOREIGN KEY (account_pubkey) REFERENCES accounts(pubkey) ON DELETE CASCADE
FOREIGN KEY (account_pubkey) REFERENCES accounts(pubkey) ON DELETE CASCADE,
PRIMARY KEY (mls_group_id, account_pubkey)
);

CREATE INDEX idx_groups_mls_group_id ON groups(mls_group_id);
CREATE INDEX idx_groups_account ON groups(account_pubkey);
CREATE INDEX idx_groups_nostr ON groups(nostr_group_id);
CREATE INDEX idx_groups_mls_group_id_account ON groups(mls_group_id, account_pubkey);

-- Invites table matching the Invite struct
CREATE TABLE invites (
event_id TEXT PRIMARY KEY, -- the event_id of the 444 unsigned invite event
Expand All @@ -68,6 +88,12 @@ CREATE TABLE invites (
FOREIGN KEY (account_pubkey) REFERENCES accounts(pubkey) ON DELETE CASCADE
);

CREATE INDEX idx_invites_mls_group ON invites(mls_group_id);
CREATE INDEX idx_invites_state ON invites(state);
CREATE INDEX idx_invites_account ON invites(account_pubkey);
CREATE INDEX idx_invites_outer_event_id ON invites(outer_event_id);
CREATE INDEX idx_invites_event_id ON invites(event_id);

-- Messages table with full-text search
CREATE TABLE messages (
event_id TEXT PRIMARY KEY,
Expand All @@ -84,14 +110,21 @@ CREATE TABLE messages (
FOREIGN KEY (account_pubkey) REFERENCES accounts(pubkey) ON DELETE CASCADE
);

CREATE INDEX idx_messages_group_time ON messages(mls_group_id, created_at);
CREATE INDEX idx_messages_account_time ON messages(account_pubkey, created_at);
CREATE INDEX idx_messages_author_time ON messages(author_pubkey, created_at);
CREATE INDEX idx_messages_processing ON messages(processed, mls_group_id);
CREATE INDEX idx_messages_outer_event_id ON messages(outer_event_id);
CREATE INDEX idx_messages_event_id ON messages(event_id);

-- Full-text search for messages
CREATE VIRTUAL TABLE messages_fts USING fts5(
content,
content='messages',
content_rowid='id'
content_rowid='event_id'
);

-- FTS triggers (fixed to use event_id instead of id)
-- FTS triggers
CREATE TRIGGER messages_ai AFTER INSERT ON messages BEGIN
INSERT INTO messages_fts(rowid, content) VALUES (new.event_id, new.content);
END;
Expand All @@ -104,19 +137,3 @@ CREATE TRIGGER messages_au AFTER UPDATE ON messages BEGIN
INSERT INTO messages_fts(messages_fts, rowid, content) VALUES('delete', old.event_id, old.content);
INSERT INTO messages_fts(rowid, content) VALUES (new.event_id, new.content);
END;

CREATE INDEX idx_relays_account ON relays(account_pubkey, relay_type);
CREATE INDEX idx_relays_group ON relays(group_id, relay_type);
CREATE INDEX idx_groups_account ON groups(account_pubkey);
CREATE INDEX idx_groups_nostr ON groups(nostr_group_id);
CREATE INDEX idx_messages_group_time ON messages(mls_group_id, created_at);
CREATE INDEX idx_messages_account_time ON messages(account_pubkey, created_at);
CREATE INDEX idx_messages_author_time ON messages(author_pubkey, created_at);
CREATE INDEX idx_messages_processing ON messages(processed, mls_group_id);
CREATE INDEX idx_invites_mls_group ON invites(mls_group_id);
CREATE INDEX idx_invites_state ON invites(state);
CREATE INDEX idx_invites_account ON invites(account_pubkey);
CREATE INDEX idx_invites_outer_event_id ON invites(outer_event_id);
CREATE INDEX idx_invites_event_id ON invites(event_id);
CREATE INDEX idx_messages_outer_event_id ON messages(outer_event_id);
CREATE INDEX idx_messages_event_id ON messages(event_id);
100 changes: 69 additions & 31 deletions src-tauri/src/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,6 @@ impl Account {
tracing::debug!(target: "whitenoise::accounts", "Adding account for pubkey: {}", pubkey.to_hex());

// Fetch metadata & relays from Nostr
// We only fetch the data at this point to store on the account
// We'll add and connect to the relays in another step
let metadata = wn
.nostr
.fetch_user_metadata(pubkey)
Expand All @@ -153,12 +151,12 @@ impl Account {
.await
.map_err(AccountError::NostrManagerError);
tracing::debug!(target: "whitenoise::accounts", "Fetched key package relays for pubkey: {}", pubkey.to_hex());
// let key_packages = wn
// .nostr
// .fetch_user_key_packages(pubkey)
// .await
// .map_err(AccountError::NostrManagerError)?;
// tracing::debug!(target: "whitenoise::accounts", "Fetched key packages for pubkey: {}", pubkey.to_hex());
let key_packages = wn
.nostr
.fetch_user_key_packages(pubkey)
.await
.map_err(AccountError::NostrManagerError)?;
tracing::debug!(target: "whitenoise::accounts", "Fetched key packages for pubkey: {}", pubkey.to_hex());

let mut onboarding = AccountOnboarding::default();

Expand All @@ -177,9 +175,9 @@ impl Account {
if !key_package_relays_unwrapped.is_empty() {
onboarding.key_package_relays = true;
}
// if !key_packages.is_empty() {
// onboarding.publish_key_package = true;
// }
if !key_packages.is_empty() {
onboarding.publish_key_package = true;
}

tracing::debug!(target: "whitenoise::accounts", "Creating account with metadata: {:?}", unwrapped_metadata);

Expand All @@ -193,15 +191,20 @@ impl Account {
active: false,
};

tracing::debug!(target: "whitenoise::accounts", "Saving account to database: {:?}", account);
tracing::debug!(target: "whitenoise::accounts", "Saving new account to database");
account.save(wn.clone()).await?;

tracing::debug!(target: "whitenoise::accounts", "Inserting nostr relays, {:?}", nostr_relays_unwrapped);
account
.update_relays(RelayType::Nostr, &nostr_relays_unwrapped, wn.clone())
.await?;

tracing::debug!(target: "whitenoise::accounts", "Inserting inbox relays, {:?}", inbox_relays_unwrapped);
account
.update_relays(RelayType::Inbox, &inbox_relays_unwrapped, wn.clone())
.await?;

tracing::debug!(target: "whitenoise::accounts", "Inserting key package relays, {:?}", key_package_relays_unwrapped);
account
.update_relays(
RelayType::KeyPackage,
Expand All @@ -210,10 +213,10 @@ impl Account {
)
.await?;

tracing::debug!(target: "whitenoise::accounts", "Storing private key");
secrets_store::store_private_key(keys, &wn.data_dir)?;

tracing::debug!(target: "whitenoise::accounts", "Account added from keys and secret saved");

// Set active if requested
if set_active {
account.set_active(wn.clone(), app_handle).await?;
}
Expand Down Expand Up @@ -503,16 +506,23 @@ impl Account {
relays: &Vec<String>,
wn: tauri::State<'_, Whitenoise>,
) -> Result<Account> {
if relays.is_empty() {
return Ok(self.clone());
}

let mut txn = wn.database.pool.begin().await?;

// Then insert the new relays
for relay in relays {
sqlx::query("INSERT OR REPLACE INTO relays (url, relay_type, account_pubkey, group_id) VALUES (?, ?, ?, ?)")
.bind(relay)
.bind(String::from(relay_type))
.bind(self.pubkey.to_hex().as_str())
.bind(Option::<Vec<u8>>::None)
.execute(&mut *txn)
.await?;
sqlx::query(
"INSERT OR REPLACE INTO account_relays (url, relay_type, account_pubkey)
VALUES (?, ?, ?)",
)
.bind(relay)
.bind(String::from(relay_type))
.bind(self.pubkey.to_hex())
.execute(&mut *txn)
.await?;
}

txn.commit().await?;
Expand All @@ -522,21 +532,49 @@ impl Account {

/// Saves the account to the database
pub async fn save(&self, wn: tauri::State<'_, Whitenoise>) -> Result<Account> {
tracing::debug!(
target: "whitenoise::accounts::save",
"Beginning save transaction for pubkey: {}",
self.pubkey.to_hex()
);

let mut txn = wn.database.pool.begin().await?;

sqlx::query("INSERT OR REPLACE INTO accounts (pubkey, metadata, settings, onboarding, last_used, last_synced, active) VALUES (?, ?, ?, ?, ?, ?, ?)")
.bind(self.pubkey.to_hex().as_str())
.bind(&serde_json::to_string(&self.metadata)?)
.bind(&serde_json::to_string(&self.settings)?)
.bind(&serde_json::to_string(&self.onboarding)?)
.bind(self.last_used.to_string())
.bind(self.last_synced.to_string())
.bind(self.active)
.execute(&mut *txn)
.await?;
let result = sqlx::query(
"INSERT INTO accounts (pubkey, metadata, settings, onboarding, last_used, last_synced, active)
VALUES (?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(pubkey) DO UPDATE SET
metadata = excluded.metadata,
settings = excluded.settings,
onboarding = excluded.onboarding,
last_used = excluded.last_used,
last_synced = excluded.last_synced,
active = excluded.active"
)
.bind(self.pubkey.to_hex())
.bind(&serde_json::to_string(&self.metadata)?)
.bind(&serde_json::to_string(&self.settings)?)
.bind(&serde_json::to_string(&self.onboarding)?)
.bind(self.last_used.to_string())
.bind(self.last_synced.to_string())
.bind(self.active)
.execute(&mut *txn)
.await?;

tracing::debug!(
target: "whitenoise::accounts::save",
"Query executed. Rows affected: {}",
result.rows_affected()
);

txn.commit().await?;

tracing::debug!(
target: "whitenoise::accounts::save",
"Transaction committed successfully for pubkey: {}",
self.pubkey.to_hex()
);

Ok(self.clone())
}

Expand Down
36 changes: 2 additions & 34 deletions src-tauri/src/commands/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,6 @@ use crate::accounts::Account;
use crate::whitenoise::Whitenoise;
use nostr_sdk::prelude::*;

/// Retrieves the currently active account.
///
/// # Arguments
///
/// * `wn` - A reference to the Whitenoise state.
///
/// # Returns
///
/// * `Ok(Option<Account>)` - The active account if it exists.
/// * `Err(String)` - An error message if there was an issue fetching the active account.
#[tauri::command]
pub async fn get_active_account(wn: tauri::State<'_, Whitenoise>) -> Result<Account, String> {
tracing::debug!(target: "whitenoise::commands::accounts", "Getting active account");

let result = Account::get_active(wn.clone())
.await
.map_err(|e| {
tracing::error!(target: "whitenoise::commands::accounts", "Error getting active account: {}", e);
e.to_string()
});

tracing::debug!(target: "whitenoise::commands::accounts", "Get active account result: {:?}", result);

result
}

/// Lists all accounts.
///
/// # Arguments
Expand Down Expand Up @@ -134,16 +108,10 @@ pub async fn set_active_account(

account.active = true;

tracing::debug!(target: "whitenoise::commands::accounts", "Found account: {:?}", account);

let result = account
account
.set_active(wn.clone(), &app_handle)
.await
.map_err(|e| format!("Error setting active account: {}", e));

tracing::debug!(target: "whitenoise::commands::accounts", "Set active result: {:?}", result);

result
.map_err(|e| format!("Error setting active account: {}", e))
}

/// Logs out the specified account.
Expand Down
22 changes: 15 additions & 7 deletions src-tauri/src/commands/groups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,28 @@ pub async fn get_group_and_messages(
) -> Result<(Group, Vec<UnsignedEvent>), String> {
let mls_group_id =
hex::decode(group_id).map_err(|e| format!("Error decoding group id: {}", e))?;
tracing::debug!(
target: "whitenoise::commands::groups::get_group_and_messages",
"Getting group and messages for group ID: {:?}",
mls_group_id
);
let group = Group::find_by_mls_group_id(&mls_group_id, wn.clone())
.await
.map_err(|e| format!("Error fetching group: {}", e))?;
tracing::debug!(
target: "whitenoise::commands::groups::get_group_and_messages",
"Group: {:?}",
group
);
let messages = group
.messages(wn.clone())
.await
.map_err(|e| format!("Error fetching messages: {}", e))?;
tracing::debug!(
target: "whitenoise::commands::groups::get_group_and_messages",
"Messages: {:?}",
messages
);
Ok((group, messages))
}

Expand Down Expand Up @@ -341,7 +356,6 @@ pub async fn create_group(
let group_id = mls_group.group_id().to_vec();

// Create the group and save it to the database
// Also adds the group to the active account
let nostr_group = Group::new(
group_id.clone(),
mls_group.epoch().as_u64(),
Expand Down Expand Up @@ -377,12 +391,6 @@ pub async fn create_group(
.emit("group_added", nostr_group.clone())
.map_err(|e| e.to_string())?;

tracing::debug!(
target: "whitenoise::groups::create_group",
"Added MLS group id to account manager: {:?}",
group_id
);

Ok(nostr_group)
}

Expand Down
Loading

0 comments on commit 4c0061a

Please sign in to comment.