use rusqlite::Connection;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct User {
pub id: String,
pub email: String,
pub name: String,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Team {
pub id: String,
pub name: String,
pub owner_id: String,
pub created_at: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TeamMember {
pub user_id: String,
pub role: String,
pub joined_at: u64,
pub name: String,
pub email: String,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Invite {
pub token: String,
pub team_id: String,
pub team_name: String,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Channel {
pub id: String,
pub name: String,
pub team_id: String,
}
pub fn init_hub_tables(conn: &Connection) {
conn.execute_batch(
"CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
created_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS api_keys (
key_hash TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES users(id),
label TEXT NOT NULL DEFAULT 'default',
created_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS teams (
id TEXT PRIMARY KEY,
name TEXT UNIQUE NOT NULL,
owner_id TEXT NOT NULL REFERENCES users(id),
created_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS team_members (
team_id TEXT NOT NULL REFERENCES teams(id),
user_id TEXT NOT NULL REFERENCES users(id),
role TEXT NOT NULL DEFAULT 'member',
joined_at INTEGER NOT NULL,
PRIMARY KEY (team_id, user_id)
);
CREATE TABLE IF NOT EXISTS invites (
token TEXT PRIMARY KEY,
team_id TEXT NOT NULL REFERENCES teams(id),
inviter_id TEXT NOT NULL REFERENCES users(id),
email TEXT,
created_at INTEGER NOT NULL,
used_at INTEGER
);
CREATE TABLE IF NOT EXISTS channels (
id TEXT PRIMARY KEY,
team_id TEXT NOT NULL REFERENCES teams(id),
name TEXT NOT NULL,
created_at INTEGER NOT NULL,
UNIQUE(team_id, name)
);",
)
.expect("Failed to create hub tables");
let _ = conn.execute_batch("ALTER TABLE messages ADD COLUMN team_id TEXT;");
let _ = conn.execute_batch("ALTER TABLE messages ADD COLUMN channel TEXT DEFAULT 'general';");
}
pub fn hash_api_key(key: &str) -> String {
let mut hasher = Sha256::new();
hasher.update(key.as_bytes());
hex::encode(hasher.finalize())
}
pub fn generate_api_key() -> String {
format!("ar_{}", uuid::Uuid::new_v4().to_string().replace('-', ""))
}
pub fn verify_api_key(conn: &Connection, key: &str) -> Option<User> {
let key_hash = hash_api_key(key);
conn.query_row(
"SELECT u.id, u.email, u.name
FROM api_keys ak
JOIN users u ON u.id = ak.user_id
WHERE ak.key_hash = ?1",
[&key_hash],
|row| {
Ok(User {
id: row.get(0)?,
email: row.get(1)?,
name: row.get(2)?,
})
},
)
.ok()
}