Compare commits
2 Commits
2514aa8413
...
5cd1f4f736
Author | SHA1 | Date |
---|---|---|
|
5cd1f4f736 | |
|
c48d2a78ca |
|
@ -77,3 +77,9 @@ pub struct Page {
|
|||
pub struct UserFilter {
|
||||
pub username: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SignupLink {
|
||||
pub id: i64,
|
||||
pub time_created: DateTime<Utc>,
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
use chrono::Utc;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::{AuthErr, Page, models};
|
||||
|
||||
/// Admin view of the repository, providing methods only allowed by admins
|
||||
pub struct AdminRepository<'a> {
|
||||
pub(crate) store: &'a (dyn super::GpodderStore + Send + Sync),
|
||||
pub(crate) user: &'a models::User,
|
||||
pub(crate) _user: &'a models::User,
|
||||
}
|
||||
|
||||
impl<'a> AdminRepository<'a> {
|
||||
|
@ -14,4 +17,21 @@ impl<'a> AdminRepository<'a> {
|
|||
) -> Result<Vec<models::User>, AuthErr> {
|
||||
self.store.paginated_users(page, filter)
|
||||
}
|
||||
|
||||
/// Generate a new unique signup link ID
|
||||
pub fn generate_signup_link(&self) -> Result<models::SignupLink, AuthErr> {
|
||||
let link = models::SignupLink {
|
||||
id: rand::thread_rng().r#gen(),
|
||||
time_created: Utc::now(),
|
||||
};
|
||||
|
||||
self.store.insert_signup_link(&link)?;
|
||||
|
||||
Ok(link)
|
||||
}
|
||||
|
||||
/// Remove the signup link with the given ID, if it exists
|
||||
pub fn remove_signup_link(&self, id: i64) -> Result<bool, AuthErr> {
|
||||
self.store.remove_signup_link(id)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ impl GpodderRepository {
|
|||
if user.admin {
|
||||
Ok(admin::AdminRepository {
|
||||
store: self.store.as_ref(),
|
||||
user,
|
||||
_user: user,
|
||||
})
|
||||
} else {
|
||||
Err(AuthErr::NotAnAdmin)
|
||||
|
|
|
@ -67,6 +67,35 @@ pub trait GpodderAuthStore {
|
|||
|
||||
/// Return the given page of users, ordered by username
|
||||
fn paginated_users(&self, page: Page, filter: &UserFilter) -> Result<Vec<User>, AuthErr>;
|
||||
|
||||
/// Insert the signup link into the database.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If a database failure occurs
|
||||
fn insert_signup_link(&self, link: &SignupLink) -> Result<(), AuthErr>;
|
||||
|
||||
/// Get the signup link associated with the given ID
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Some(link) if the ID corresponds to an existing signup link; None otherwise
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If a database failure occurs
|
||||
fn get_signup_link(&self, id: i64) -> Result<Option<SignupLink>, AuthErr>;
|
||||
|
||||
/// Remove the signup link with the given ID.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// True if the ID existed in the database; false otherwise
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If a database failure occurs
|
||||
fn remove_signup_link(&self, id: i64) -> Result<bool, AuthErr>;
|
||||
}
|
||||
|
||||
pub trait GpodderDeviceStore {
|
||||
|
|
|
@ -15,8 +15,15 @@ tracing = { workspace = true }
|
|||
chrono = { workspace = true, features = ["serde"] }
|
||||
|
||||
libsqlite3-sys = { version = "0.31.0", features = ["bundled"] }
|
||||
diesel = { version = "2.2.7", features = ["r2d2", "sqlite", "returning_clauses_for_sqlite_3_35"] }
|
||||
diesel_migrations = { version = "2.2.0", features = ["sqlite"] }
|
||||
|
||||
[dependencies.diesel]
|
||||
version = "2.2.7"
|
||||
features = [
|
||||
"r2d2",
|
||||
"sqlite",
|
||||
"returning_clauses_for_sqlite_3_35",
|
||||
]
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.5.1"
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table signup_links;
|
|
@ -0,0 +1,5 @@
|
|||
-- Your SQL goes here
|
||||
create table signup_links (
|
||||
id bigint primary key not null,
|
||||
created_at bigint not null
|
||||
);
|
|
@ -2,5 +2,6 @@ pub mod device;
|
|||
pub mod device_subscription;
|
||||
pub mod episode_action;
|
||||
pub mod session;
|
||||
pub mod signup_link;
|
||||
pub mod sync_group;
|
||||
pub mod user;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
use diesel::prelude::*;
|
||||
|
||||
use crate::schema::*;
|
||||
|
||||
#[derive(Clone, Queryable, Selectable, Insertable)]
|
||||
#[diesel(table_name = signup_links)]
|
||||
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
|
||||
pub struct SignupLink {
|
||||
pub id: i64,
|
||||
pub created_at: i64,
|
||||
}
|
|
@ -7,6 +7,7 @@ use crate::{
|
|||
DbError,
|
||||
models::{
|
||||
session::Session,
|
||||
signup_link::SignupLink,
|
||||
user::{NewUser, User},
|
||||
},
|
||||
schema::*,
|
||||
|
@ -23,6 +24,15 @@ impl From<User> for gpodder::User {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<SignupLink> for gpodder::SignupLink {
|
||||
fn from(value: SignupLink) -> Self {
|
||||
Self {
|
||||
id: value.id,
|
||||
time_created: DateTime::from_timestamp(value.created_at, 0).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl gpodder::GpodderAuthStore for SqliteRepository {
|
||||
fn get_user(&self, username: &str) -> Result<Option<gpodder::models::User>, AuthErr> {
|
||||
Ok(users::table
|
||||
|
@ -170,4 +180,39 @@ impl gpodder::GpodderAuthStore for SqliteRepository {
|
|||
})()
|
||||
.map_err(AuthErr::from)
|
||||
}
|
||||
|
||||
fn get_signup_link(&self, id: i64) -> Result<Option<gpodder::SignupLink>, AuthErr> {
|
||||
match signup_links::table
|
||||
.find(id)
|
||||
.select(SignupLink::as_select())
|
||||
.first(&mut self.pool.get().map_err(DbError::from)?)
|
||||
.optional()
|
||||
{
|
||||
Ok(Some(link)) => Ok(Some(gpodder::SignupLink::from(link))),
|
||||
Ok(None) => Ok(None),
|
||||
Err(err) => Err(DbError::from(err).into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_signup_link(&self, link: &gpodder::SignupLink) -> Result<(), AuthErr> {
|
||||
diesel::insert_into(signup_links::table)
|
||||
.values(SignupLink {
|
||||
id: link.id,
|
||||
created_at: link.time_created.timestamp(),
|
||||
})
|
||||
.execute(&mut self.pool.get().map_err(DbError::from)?)
|
||||
.map_err(DbError::from)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove_signup_link(&self, id: i64) -> Result<bool, AuthErr> {
|
||||
match diesel::delete(signup_links::table.filter(signup_links::id.eq(id)))
|
||||
.execute(&mut self.pool.get().map_err(DbError::from)?)
|
||||
{
|
||||
Ok(0) => Ok(false),
|
||||
Ok(_) => Ok(true),
|
||||
Err(err) => Err(DbError::from(err).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,13 @@ diesel::table! {
|
|||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
signup_links (id) {
|
||||
id -> BigInt,
|
||||
created_at -> BigInt,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
sync_groups (id) {
|
||||
id -> BigInt,
|
||||
|
@ -74,6 +81,7 @@ diesel::allow_tables_to_appear_in_same_query!(
|
|||
devices,
|
||||
episode_actions,
|
||||
sessions,
|
||||
signup_links,
|
||||
sync_groups,
|
||||
users,
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue