feat: started repository abstraction

episode-actions
Jef Roosens 2025-02-27 21:39:38 +01:00
parent d866d23efa
commit 952f92c178
No known key found for this signature in database
GPG Key ID: 21FD3D77D56BAF49
8 changed files with 128 additions and 1 deletions

View File

@ -32,7 +32,10 @@ impl ServeCommand {
let pool = db::initialize_db(cli.data_dir.join(crate::DB_FILENAME), true).unwrap();
let ctx = server::Context { pool };
let ctx = server::Context {
pool: pool.clone(),
repo: db::SqliteRepository::from(pool.clone()),
};
let app = server::app(ctx);
let rt = tokio::runtime::Builder::new_multi_thread()

View File

@ -1,4 +1,5 @@
pub mod models;
mod repository;
mod schema;
pub use models::device::{Device, DeviceType, NewDevice};
@ -6,6 +7,8 @@ pub use models::session::Session;
pub use models::subscription::{NewSubscription, Subscription};
pub use models::user::{NewUser, User};
pub use repository::SqliteRepository;
use diesel::{
r2d2::{ConnectionManager, Pool},
SqliteConnection,

View File

@ -0,0 +1,75 @@
use diesel::prelude::*;
use rand::Rng;
use super::SqliteRepository;
use crate::{
db::{self, schema::*},
gpodder,
};
impl From<diesel::r2d2::PoolError> for gpodder::AuthErr {
fn from(value: diesel::r2d2::PoolError) -> Self {
Self::Other(Box::new(value))
}
}
impl From<diesel::result::Error> for gpodder::AuthErr {
fn from(value: diesel::result::Error) -> Self {
Self::Other(Box::new(value))
}
}
impl gpodder::AuthRepository for SqliteRepository {
fn validate_session(&self, session_id: i64) -> Result<gpodder::User, gpodder::AuthErr> {
match sessions::dsl::sessions
.inner_join(users::table)
.filter(sessions::id.eq(session_id))
.select(db::User::as_select())
.get_result(&mut self.pool.get()?)
{
Ok(user) => Ok(gpodder::User {
id: user.id,
username: user.username,
}),
Err(diesel::result::Error::NotFound) => Err(gpodder::AuthErr::UnknownSession),
Err(err) => Err(gpodder::AuthErr::Other(Box::new(err))),
}
}
fn create_session(
&self,
username: &str,
password: &str,
) -> Result<(i64, gpodder::models::User), gpodder::AuthErr> {
if let Some(user) = users::table
.select(db::User::as_select())
.filter(users::username.eq(username))
.first(&mut self.pool.get()?)
.optional()?
{
if user.verify_password(password) {
let id: i64 = rand::thread_rng().gen();
let session_id = db::Session {
id,
user_id: user.id,
}
.insert_into(sessions::table)
.returning(sessions::id)
.get_result(&mut self.pool.get()?)?;
Ok((
session_id,
gpodder::User {
id: user.id,
username: user.username,
},
))
} else {
Err(gpodder::AuthErr::InvalidPassword)
}
} else {
Err(gpodder::AuthErr::UnknownUser)
}
}
}

View File

@ -0,0 +1,14 @@
mod auth;
use super::DbPool;
#[derive(Clone)]
pub struct SqliteRepository {
pool: DbPool,
}
impl From<DbPool> for SqliteRepository {
fn from(value: DbPool) -> Self {
Self { pool: value }
}
}

26
src/gpodder/mod.rs 100644
View File

@ -0,0 +1,26 @@
pub mod models;
pub use models::*;
pub enum AuthErr {
UnknownSession,
UnknownUser,
InvalidPassword,
Other(Box<dyn std::error::Error>),
}
pub trait AuthRepository: Send + Sync {
/// Validate the given session ID and return its user.
fn validate_session(&self, session_id: i64) -> Result<models::User, AuthErr>;
/// Create a new session for the given user.
fn create_session(
&self,
username: &str,
password: &str,
) -> Result<(i64, models::User), AuthErr>;
}
// pub trait DeviceRepository: Send + Sync {
// fn devices_for_user(&self, )
// }

View File

@ -0,0 +1,4 @@
pub struct User {
pub id: i64,
pub username: String,
}

View File

@ -1,5 +1,6 @@
mod cli;
mod db;
mod gpodder;
mod server;
use clap::Parser;

View File

@ -7,6 +7,7 @@ use tower_http::trace::TraceLayer;
#[derive(Clone)]
pub struct Context {
pub pool: crate::db::DbPool,
pub repo: crate::db::SqliteRepository,
}
pub fn app(ctx: Context) -> Router {