use chrono::DateTime; use diesel::prelude::*; use gpodder::AuthErr; use super::SqliteRepository; use crate::{ models::{ session::Session, user::{NewUser, User}, }, schema::*, DbError, }; impl From for gpodder::User { fn from(value: User) -> Self { Self { id: value.id, username: value.username, password_hash: value.password_hash, } } } impl gpodder::AuthStore for SqliteRepository { fn get_user(&self, username: &str) -> Result, AuthErr> { Ok(users::table .select(User::as_select()) .filter(users::username.eq(username)) .first(&mut self.pool.get().map_err(DbError::from)?) .optional() .map_err(DbError::from)? .map(gpodder::User::from)) } fn insert_user(&self, username: &str, password_hash: &str) -> Result { let conn = &mut self.pool.get().map_err(DbError::from)?; Ok(diesel::insert_into(users::table) .values(NewUser { username, password_hash, }) .returning(User::as_returning()) .get_result(conn) .map(gpodder::User::from) .map_err(DbError::from)?) } fn get_session(&self, session_id: i64) -> Result, AuthErr> { match sessions::table .inner_join(users::table) .filter(sessions::id.eq(session_id)) .select((Session::as_select(), User::as_select())) .get_result(&mut self.pool.get().map_err(DbError::from)?) .optional() { Ok(Some((session, user))) => Ok(Some(gpodder::Session { id: session.id, last_seen: DateTime::from_timestamp(session.last_seen, 0).unwrap(), user: user.into(), user_agent: session.user_agent.clone(), })), Ok(None) => Ok(None), Err(err) => Err(DbError::from(err).into()), } } fn remove_session(&self, session_id: i64) -> Result<(), AuthErr> { Ok( diesel::delete(sessions::table.filter(sessions::id.eq(session_id))) .execute(&mut self.pool.get().map_err(DbError::from)?) .map(|_| ()) .map_err(DbError::from)?, ) } fn insert_session(&self, session: &gpodder::Session) -> Result<(), AuthErr> { Ok(Session { id: session.id, user_id: session.user.id, last_seen: session.last_seen.timestamp(), user_agent: session.user_agent.clone(), } .insert_into(sessions::table) .execute(&mut self.pool.get().map_err(DbError::from)?) .map(|_| ()) .map_err(DbError::from)?) } fn refresh_session( &self, session: &gpodder::Session, timestamp: DateTime, ) -> Result<(), AuthErr> { if diesel::update(sessions::table.filter(sessions::id.eq(session.id))) .set(sessions::last_seen.eq(timestamp.timestamp())) .execute(&mut self.pool.get().map_err(DbError::from)?) .map_err(DbError::from)? == 0 { Err(AuthErr::UnknownSession) } else { Ok(()) } } fn remove_old_sessions(&self, min_last_seen: DateTime) -> Result { let min_last_seen = min_last_seen.timestamp(); Ok( diesel::delete(sessions::table.filter(sessions::last_seen.lt(min_last_seen))) .execute(&mut self.pool.get().map_err(DbError::from)?) .map_err(DbError::from)?, ) } fn paginated_sessions( &self, user: &gpodder::User, page: gpodder::Page, ) -> Result, AuthErr> { (|| { let sessions = sessions::table .filter(sessions::user_id.eq(user.id)) .order(sessions::last_seen.desc()) .offset((page.page * page.per_page) as i64) .limit(page.per_page as i64) .select(Session::as_select()) .get_results(&mut self.pool.get()?)? .into_iter() .map(|session| gpodder::Session { id: session.id, last_seen: DateTime::from_timestamp(session.last_seen, 0).unwrap(), user_agent: session.user_agent, user: user.clone(), }) .collect(); Ok::<_, DbError>(sessions) })() .map_err(AuthErr::from) } }