refactor: split up gpodder module files
parent
0cfcd90eba
commit
2a8917f21d
|
@ -1,166 +1,9 @@
|
||||||
pub mod models;
|
pub mod models;
|
||||||
mod repository;
|
mod repository;
|
||||||
|
mod store;
|
||||||
|
|
||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
|
||||||
pub use models::*;
|
pub use models::*;
|
||||||
|
|
||||||
pub use repository::GpodderRepository;
|
pub use repository::GpodderRepository;
|
||||||
|
pub use store::{
|
||||||
#[derive(Debug)]
|
AuthErr, AuthStore, DeviceRepository, EpisodeActionRepository, Store, SubscriptionRepository,
|
||||||
pub enum AuthErr {
|
};
|
||||||
UnknownSession,
|
|
||||||
UnknownUser,
|
|
||||||
InvalidPassword,
|
|
||||||
Other(Box<dyn std::error::Error + Sync + Send>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for AuthErr {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::UnknownUser => write!(f, "unknown user"),
|
|
||||||
Self::UnknownSession => write!(f, "unknown session"),
|
|
||||||
Self::InvalidPassword => write!(f, "invalid password"),
|
|
||||||
Self::Other(err) => err.fmt(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for AuthErr {}
|
|
||||||
|
|
||||||
pub trait Store:
|
|
||||||
AuthStore + DeviceRepository + SubscriptionRepository + EpisodeActionRepository
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Store for T where
|
|
||||||
T: AuthStore + DeviceRepository + SubscriptionRepository + EpisodeActionRepository
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait AuthStore {
|
|
||||||
/// Retrieve the session with the given session ID
|
|
||||||
fn get_session(&self, session_id: i64) -> Result<Option<models::Session>, AuthErr>;
|
|
||||||
|
|
||||||
/// Retrieve the user with the given username
|
|
||||||
fn get_user(&self, username: &str) -> Result<Option<models::User>, AuthErr>;
|
|
||||||
|
|
||||||
/// Create a new session for a user with the given session ID
|
|
||||||
fn insert_session(&self, session: &Session) -> Result<(), AuthErr>;
|
|
||||||
|
|
||||||
/// Remove the session with the given session ID
|
|
||||||
fn remove_session(&self, session_id: i64) -> Result<(), AuthErr>;
|
|
||||||
|
|
||||||
/// Update the session's timestamp
|
|
||||||
fn refresh_session(&self, session: &Session, timestamp: DateTime<Utc>) -> Result<(), AuthErr>;
|
|
||||||
|
|
||||||
/// Remove any sessions whose last_seen timestamp is before the given minimum value
|
|
||||||
fn remove_old_sessions(&self, min_last_seen: DateTime<Utc>) -> Result<usize, AuthErr>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait DeviceRepository {
|
|
||||||
/// Return all devices associated with the user
|
|
||||||
fn devices_for_user(&self, user: &User) -> Result<Vec<Device>, AuthErr>;
|
|
||||||
|
|
||||||
/// Update the information for the given device. If the device doesn't exist yet, it should be
|
|
||||||
/// created without a sync group.
|
|
||||||
fn update_device_info(
|
|
||||||
&self,
|
|
||||||
user: &User,
|
|
||||||
device_id: &str,
|
|
||||||
patch: DevicePatch,
|
|
||||||
) -> Result<(), AuthErr>;
|
|
||||||
|
|
||||||
/// Add the devices to the same sync group by:
|
|
||||||
///
|
|
||||||
/// * Merging the sync groups of all devices already in a sync group
|
|
||||||
/// * Adding all devices not yet in a sync group to the newly merged sync group
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// ID of the final sync group
|
|
||||||
fn merge_sync_groups(&self, user: &User, device_ids: Vec<&str>) -> Result<i64, AuthErr>;
|
|
||||||
|
|
||||||
/// Synchronize the sync group by adding or removing subscriptions to each device so that each
|
|
||||||
/// device's subscription state is the same
|
|
||||||
fn synchronize_sync_group(
|
|
||||||
&self,
|
|
||||||
group_id: i64,
|
|
||||||
time_changed: DateTime<Utc>,
|
|
||||||
) -> Result<(), AuthErr>;
|
|
||||||
|
|
||||||
/// Remove the devices from their respective sync groups
|
|
||||||
fn remove_from_sync_group(&self, user: &User, device_ids: Vec<&str>) -> Result<(), AuthErr>;
|
|
||||||
|
|
||||||
/// Return all device IDs for the user, grouped per sync group
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// A tuple (unsynced devices, sync groups)
|
|
||||||
fn devices_by_sync_group(
|
|
||||||
&self,
|
|
||||||
user: &User,
|
|
||||||
) -> Result<(Vec<String>, Vec<Vec<String>>), AuthErr>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait SubscriptionRepository {
|
|
||||||
/// Return the subscriptions for the given device
|
|
||||||
fn subscriptions_for_device(
|
|
||||||
&self,
|
|
||||||
user: &User,
|
|
||||||
device_id: &str,
|
|
||||||
) -> Result<Vec<models::Subscription>, AuthErr>;
|
|
||||||
|
|
||||||
/// Return all subscriptions for a given user
|
|
||||||
fn subscriptions_for_user(&self, user: &User) -> Result<Vec<models::Subscription>, AuthErr>;
|
|
||||||
|
|
||||||
/// Replace the list of subscriptions for a device and all devices in its sync group with the
|
|
||||||
/// given list
|
|
||||||
fn set_subscriptions_for_device(
|
|
||||||
&self,
|
|
||||||
user: &User,
|
|
||||||
device_id: &str,
|
|
||||||
urls: Vec<String>,
|
|
||||||
time_changed: DateTime<Utc>,
|
|
||||||
) -> Result<(), AuthErr>;
|
|
||||||
|
|
||||||
/// Update the list of subscriptions for a device and all devices in its sync group by adding
|
|
||||||
/// and removing the given URLs
|
|
||||||
fn update_subscriptions_for_device(
|
|
||||||
&self,
|
|
||||||
user: &User,
|
|
||||||
device_id: &str,
|
|
||||||
add: Vec<String>,
|
|
||||||
remove: Vec<String>,
|
|
||||||
time_changed: DateTime<Utc>,
|
|
||||||
) -> Result<(), AuthErr>;
|
|
||||||
|
|
||||||
/// Returns the changes in subscriptions since the given timestamp.
|
|
||||||
fn subscription_updates_for_device(
|
|
||||||
&self,
|
|
||||||
user: &User,
|
|
||||||
device_id: &str,
|
|
||||||
since: DateTime<Utc>,
|
|
||||||
) -> Result<(Vec<Subscription>, Vec<Subscription>), AuthErr>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait EpisodeActionRepository {
|
|
||||||
/// Insert the given episode actions into the datastore.
|
|
||||||
fn add_episode_actions(
|
|
||||||
&self,
|
|
||||||
user: &User,
|
|
||||||
actions: Vec<EpisodeAction>,
|
|
||||||
time_changed: DateTime<Utc>,
|
|
||||||
) -> Result<(), AuthErr>;
|
|
||||||
|
|
||||||
/// Retrieve the list of episode actions for the given user.
|
|
||||||
fn episode_actions_for_user(
|
|
||||||
&self,
|
|
||||||
user: &User,
|
|
||||||
since: Option<DateTime<Utc>>,
|
|
||||||
podcast: Option<String>,
|
|
||||||
device: Option<String>,
|
|
||||||
aggregated: bool,
|
|
||||||
) -> Result<Vec<EpisodeAction>, AuthErr>;
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,7 +4,10 @@ use argon2::{Argon2, PasswordHash, PasswordVerifier};
|
||||||
use chrono::{DateTime, TimeDelta, Utc};
|
use chrono::{DateTime, TimeDelta, Utc};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
use super::{models, AuthErr, Store};
|
use crate::{
|
||||||
|
models,
|
||||||
|
store::{AuthErr, Store},
|
||||||
|
};
|
||||||
|
|
||||||
const MAX_SESSION_AGE: i64 = 60 * 60 * 24 * 7;
|
const MAX_SESSION_AGE: i64 = 60 * 60 * 24 * 7;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use crate::models::*;
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum AuthErr {
|
||||||
|
UnknownSession,
|
||||||
|
UnknownUser,
|
||||||
|
InvalidPassword,
|
||||||
|
Other(Box<dyn std::error::Error + Sync + Send>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for AuthErr {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::UnknownUser => write!(f, "unknown user"),
|
||||||
|
Self::UnknownSession => write!(f, "unknown session"),
|
||||||
|
Self::InvalidPassword => write!(f, "invalid password"),
|
||||||
|
Self::Other(err) => err.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for AuthErr {}
|
||||||
|
|
||||||
|
pub trait Store:
|
||||||
|
AuthStore + DeviceRepository + SubscriptionRepository + EpisodeActionRepository
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Store for T where
|
||||||
|
T: AuthStore + DeviceRepository + SubscriptionRepository + EpisodeActionRepository
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait AuthStore {
|
||||||
|
/// Retrieve the session with the given session ID
|
||||||
|
fn get_session(&self, session_id: i64) -> Result<Option<Session>, AuthErr>;
|
||||||
|
|
||||||
|
/// Retrieve the user with the given username
|
||||||
|
fn get_user(&self, username: &str) -> Result<Option<User>, AuthErr>;
|
||||||
|
|
||||||
|
/// Create a new session for a user with the given session ID
|
||||||
|
fn insert_session(&self, session: &Session) -> Result<(), AuthErr>;
|
||||||
|
|
||||||
|
/// Remove the session with the given session ID
|
||||||
|
fn remove_session(&self, session_id: i64) -> Result<(), AuthErr>;
|
||||||
|
|
||||||
|
/// Update the session's timestamp
|
||||||
|
fn refresh_session(&self, session: &Session, timestamp: DateTime<Utc>) -> Result<(), AuthErr>;
|
||||||
|
|
||||||
|
/// Remove any sessions whose last_seen timestamp is before the given minimum value
|
||||||
|
fn remove_old_sessions(&self, min_last_seen: DateTime<Utc>) -> Result<usize, AuthErr>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait DeviceRepository {
|
||||||
|
/// Return all devices associated with the user
|
||||||
|
fn devices_for_user(&self, user: &User) -> Result<Vec<Device>, AuthErr>;
|
||||||
|
|
||||||
|
/// Update the information for the given device. If the device doesn't exist yet, it should be
|
||||||
|
/// created without a sync group.
|
||||||
|
fn update_device_info(
|
||||||
|
&self,
|
||||||
|
user: &User,
|
||||||
|
device_id: &str,
|
||||||
|
patch: DevicePatch,
|
||||||
|
) -> Result<(), AuthErr>;
|
||||||
|
|
||||||
|
/// Add the devices to the same sync group by:
|
||||||
|
///
|
||||||
|
/// * Merging the sync groups of all devices already in a sync group
|
||||||
|
/// * Adding all devices not yet in a sync group to the newly merged sync group
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// ID of the final sync group
|
||||||
|
fn merge_sync_groups(&self, user: &User, device_ids: Vec<&str>) -> Result<i64, AuthErr>;
|
||||||
|
|
||||||
|
/// Synchronize the sync group by adding or removing subscriptions to each device so that each
|
||||||
|
/// device's subscription state is the same
|
||||||
|
fn synchronize_sync_group(
|
||||||
|
&self,
|
||||||
|
group_id: i64,
|
||||||
|
time_changed: DateTime<Utc>,
|
||||||
|
) -> Result<(), AuthErr>;
|
||||||
|
|
||||||
|
/// Remove the devices from their respective sync groups
|
||||||
|
fn remove_from_sync_group(&self, user: &User, device_ids: Vec<&str>) -> Result<(), AuthErr>;
|
||||||
|
|
||||||
|
/// Return all device IDs for the user, grouped per sync group
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A tuple (unsynced devices, sync groups)
|
||||||
|
fn devices_by_sync_group(
|
||||||
|
&self,
|
||||||
|
user: &User,
|
||||||
|
) -> Result<(Vec<String>, Vec<Vec<String>>), AuthErr>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SubscriptionRepository {
|
||||||
|
/// Return the subscriptions for the given device
|
||||||
|
fn subscriptions_for_device(
|
||||||
|
&self,
|
||||||
|
user: &User,
|
||||||
|
device_id: &str,
|
||||||
|
) -> Result<Vec<Subscription>, AuthErr>;
|
||||||
|
|
||||||
|
/// Return all subscriptions for a given user
|
||||||
|
fn subscriptions_for_user(&self, user: &User) -> Result<Vec<Subscription>, AuthErr>;
|
||||||
|
|
||||||
|
/// Replace the list of subscriptions for a device and all devices in its sync group with the
|
||||||
|
/// given list
|
||||||
|
fn set_subscriptions_for_device(
|
||||||
|
&self,
|
||||||
|
user: &User,
|
||||||
|
device_id: &str,
|
||||||
|
urls: Vec<String>,
|
||||||
|
time_changed: DateTime<Utc>,
|
||||||
|
) -> Result<(), AuthErr>;
|
||||||
|
|
||||||
|
/// Update the list of subscriptions for a device and all devices in its sync group by adding
|
||||||
|
/// and removing the given URLs
|
||||||
|
fn update_subscriptions_for_device(
|
||||||
|
&self,
|
||||||
|
user: &User,
|
||||||
|
device_id: &str,
|
||||||
|
add: Vec<String>,
|
||||||
|
remove: Vec<String>,
|
||||||
|
time_changed: DateTime<Utc>,
|
||||||
|
) -> Result<(), AuthErr>;
|
||||||
|
|
||||||
|
/// Returns the changes in subscriptions since the given timestamp.
|
||||||
|
fn subscription_updates_for_device(
|
||||||
|
&self,
|
||||||
|
user: &User,
|
||||||
|
device_id: &str,
|
||||||
|
since: DateTime<Utc>,
|
||||||
|
) -> Result<(Vec<Subscription>, Vec<Subscription>), AuthErr>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait EpisodeActionRepository {
|
||||||
|
/// Insert the given episode actions into the datastore.
|
||||||
|
fn add_episode_actions(
|
||||||
|
&self,
|
||||||
|
user: &User,
|
||||||
|
actions: Vec<EpisodeAction>,
|
||||||
|
time_changed: DateTime<Utc>,
|
||||||
|
) -> Result<(), AuthErr>;
|
||||||
|
|
||||||
|
/// Retrieve the list of episode actions for the given user.
|
||||||
|
fn episode_actions_for_user(
|
||||||
|
&self,
|
||||||
|
user: &User,
|
||||||
|
since: Option<DateTime<Utc>>,
|
||||||
|
podcast: Option<String>,
|
||||||
|
device: Option<String>,
|
||||||
|
aggregated: bool,
|
||||||
|
) -> Result<Vec<EpisodeAction>, AuthErr>;
|
||||||
|
}
|
Loading…
Reference in New Issue