use crate::consts::ZELLIJ_PROJ_DIR;
use crate::shared::set_permissions;
use rusqlite::Connection;
use std::path::PathBuf;
#[derive(Debug)]
pub enum TokenError {
Database(rusqlite::Error),
Io(std::io::Error),
InvalidPath,
}
impl std::fmt::Display for TokenError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TokenError::Database(e) => write!(f, "Database error: {}", e),
TokenError::Io(e) => write!(f, "IO error: {}", e),
TokenError::InvalidPath => write!(f, "Invalid path"),
}
}
}
impl std::error::Error for TokenError {}
impl From<rusqlite::Error> for TokenError {
fn from(error: rusqlite::Error) -> Self {
TokenError::Database(error)
}
}
impl From<std::io::Error> for TokenError {
fn from(error: std::io::Error) -> Self {
TokenError::Io(error)
}
}
type Result<T> = std::result::Result<T, TokenError>;
fn get_db_path() -> Result<PathBuf> {
let data_dir = ZELLIJ_PROJ_DIR.data_dir();
std::fs::create_dir_all(data_dir)?;
let db_path = data_dir.join("remote_sessions.db");
Ok(db_path)
}
fn init_db(conn: &Connection) -> Result<()> {
conn.execute_batch("PRAGMA busy_timeout = 5000")?;
conn.execute(
"CREATE TABLE IF NOT EXISTS remote_sessions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
server_url TEXT UNIQUE NOT NULL,
session_token TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
last_used_at DATETIME DEFAULT CURRENT_TIMESTAMP
)",
[],
)?;
Ok(())
}
pub fn save_session_token(server_url: &str, session_token: &str) -> Result<()> {
let db_path = get_db_path()?;
let is_new = !db_path.exists();
let conn = Connection::open(&db_path)?;
init_db(&conn)?;
if is_new {
set_permissions(&db_path, 0o600)?;
}
conn.execute(
"INSERT OR REPLACE INTO remote_sessions (server_url, session_token, last_used_at)
VALUES (?1, ?2, CURRENT_TIMESTAMP)",
[server_url, session_token],
)?;
Ok(())
}
pub fn get_session_token(server_url: &str) -> Result<Option<String>> {
let db_path = get_db_path()?;
if !db_path.exists() {
return Ok(None);
}
let conn = Connection::open(db_path)?;
init_db(&conn)?;
let token = match conn.query_row(
"SELECT session_token FROM remote_sessions WHERE server_url = ?1",
[server_url],
|row| row.get::<_, String>(0),
) {
Ok(token) => Some(token),
Err(rusqlite::Error::QueryReturnedNoRows) => None,
Err(e) => return Err(TokenError::Database(e)),
};
if token.is_some() {
conn.execute(
"UPDATE remote_sessions SET last_used_at = CURRENT_TIMESTAMP WHERE server_url = ?1",
[server_url],
)?;
}
Ok(token)
}
pub fn delete_session_token(server_url: &str) -> Result<bool> {
let db_path = get_db_path()?;
if !db_path.exists() {
return Ok(false);
}
let conn = Connection::open(db_path)?;
init_db(&conn)?;
let rows_affected = conn.execute(
"DELETE FROM remote_sessions WHERE server_url = ?1",
[server_url],
)?;
Ok(rows_affected > 0)
}