feat(cli): add command to generate signup links

main
Jef Roosens 2025-08-28 14:29:00 +02:00
parent 722317603d
commit 4d44216e17
Signed by: Jef Roosens
GPG Key ID: 02D4C0997E74717B
4 changed files with 24 additions and 17 deletions

View File

@ -7,9 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased](https://git.rustybever.be/Chewing_Bever/otter) ## [Unreleased](https://git.rustybever.be/Chewing_Bever/otter)
* CLI command to add new users
* Public sign-up page (disabled by default) * Public sign-up page (disabled by default)
* Private sign-up links * Private sign-up links
* New CLI commands
* Add users
* Generate signup links
## [0.2.1](https://git.rustybever.be/Chewing_Bever/otter/src/tag/0.2.1) ## [0.2.1](https://git.rustybever.be/Chewing_Bever/otter/src/tag/0.2.1)

View File

@ -1,6 +1,3 @@
use chrono::Utc;
use rand::Rng;
use crate::{AuthErr, Page, models}; use crate::{AuthErr, Page, models};
/// Admin view of the repository, providing methods only allowed by admins /// Admin view of the repository, providing methods only allowed by admins
@ -17,16 +14,4 @@ impl<'a> AdminRepository<'a> {
) -> Result<Vec<models::User>, AuthErr> { ) -> Result<Vec<models::User>, AuthErr> {
self.store.paginated_users(page, filter) 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)
}
} }

View File

@ -5,7 +5,7 @@ use std::sync::Arc;
use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier, password_hash::SaltString}; use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier, password_hash::SaltString};
use chrono::{TimeDelta, Utc}; use chrono::{TimeDelta, Utc};
use rand::rngs::OsRng; use rand::{Rng, rngs::OsRng};
use crate::{ use crate::{
SignupLink, models, SignupLink, models,
@ -126,4 +126,16 @@ impl GpodderRepository {
pub fn remove_signup_link(&self, id: i64) -> Result<bool, AuthErr> { pub fn remove_signup_link(&self, id: i64) -> Result<bool, AuthErr> {
self.store.remove_signup_link(id) self.store.remove_signup_link(id)
} }
/// 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)
}
} }

View File

@ -18,7 +18,10 @@ pub enum Command {
#[derive(Subcommand)] #[derive(Subcommand)]
pub enum UserCommand { pub enum UserCommand {
/// Add a new user
Add { username: String, password: String }, Add { username: String, password: String },
/// Generate a signup link ID
GenerateSignupLink,
} }
impl Command { impl Command {
@ -59,6 +62,11 @@ impl UserCommand {
Self::Add { username, password } => { Self::Add { username, password } => {
store.create_user(username, password)?; store.create_user(username, password)?;
} }
Self::GenerateSignupLink => {
let link = store.generate_signup_link()?;
println!("/signup/{}", link.id);
}
} }
Ok(()) Ok(())