154 lines
6.7 KiB
Rust
154 lines
6.7 KiB
Rust
use std::{fmt::Display, sync::Arc};
|
|
|
|
use inquire::{min_length, prompt_text, Confirm, Password, Select, Text};
|
|
use sea_orm::{ActiveModelTrait, ActiveValue::{NotSet, Set}, ColumnTrait, DatabaseConnection, EntityTrait, ModelTrait, QueryFilter};
|
|
|
|
use crate::{entities::{owner, prelude::User, user::{self, ActiveModel}}, utils::auth::hash_password};
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
enum Action {
|
|
Add,
|
|
Delete,
|
|
Update
|
|
}
|
|
|
|
impl Action {
|
|
const VARIANTS: &'static [Action] = &[
|
|
Self::Add,
|
|
Self::Delete,
|
|
Self::Update,
|
|
];
|
|
}
|
|
|
|
impl Display for Action {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{self:?}")
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
enum Update {
|
|
Username,
|
|
Password
|
|
}
|
|
|
|
impl Update {
|
|
const VARIANTS: &'static [Update] = &[
|
|
Self::Username,
|
|
Self::Password,
|
|
];
|
|
}
|
|
|
|
impl Display for Update {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{self:?}")
|
|
}
|
|
}
|
|
|
|
pub async fn manage_users(db: Arc<DatabaseConnection>) {
|
|
loop {
|
|
match Select::new("User Manager (ESC to quit)", Action::VARIANTS.to_vec()).prompt_skippable() {
|
|
Ok(Some(action)) => {
|
|
match action {
|
|
Action::Add => {
|
|
let username = Text::new("Username").with_validator(min_length!(3)).prompt().unwrap();
|
|
if User::find().filter(user::Column::Username.eq(username.clone())).one(db.as_ref()).await.is_ok_and(|r| r.is_some()) {
|
|
println!("Username already in use !");
|
|
} else {
|
|
let password = Password::new("Password")
|
|
.with_validator(min_length!(10))
|
|
.with_display_mode(inquire::PasswordDisplayMode::Masked)
|
|
.prompt().unwrap();
|
|
let new_user = user::ActiveModel {
|
|
id: NotSet,
|
|
username: Set(username),
|
|
hashed_password: Set(hash_password(password))
|
|
};
|
|
let res = new_user.clone().insert(db.as_ref()).await.unwrap();
|
|
|
|
println!("Create corresponding owner:");
|
|
let first_name = prompt_text("First Name: ").unwrap();
|
|
let last_name = prompt_text("Last Name: ").unwrap();
|
|
let contact = prompt_text("Contact: ").unwrap();
|
|
let new_owner = owner::ActiveModel {
|
|
id: NotSet,
|
|
user_id: Set(res.id),
|
|
first_name: Set(first_name),
|
|
last_name: Set(last_name),
|
|
contact: Set(contact),
|
|
user: Set(true)
|
|
};
|
|
let _ = new_owner.insert(db.as_ref()).await.unwrap();
|
|
}
|
|
},
|
|
Action::Delete => {
|
|
match select_user(db.clone()).await {
|
|
Some(user) => {
|
|
let username = user.username.clone();
|
|
|
|
match Confirm::new(format!("Delete {username} ?").as_ref())
|
|
.with_default(false)
|
|
.with_help_message("The user and all associated data will *permanently* be deleted")
|
|
.prompt() {
|
|
Ok(true) => {
|
|
let _ = user.delete(db.as_ref()).await;
|
|
println!("{username} has been permanently deleted")
|
|
},
|
|
Ok(false) | Err(_) => println!("Cancelled deletion of {username}"),
|
|
}
|
|
},
|
|
None => println!("Could not find user")
|
|
}
|
|
},
|
|
Action::Update => {
|
|
match select_user(db.clone()).await {
|
|
Some(user) => {
|
|
match Select::new(format!("Editing {}", user.username).as_ref(), Update::VARIANTS.to_vec()).prompt() {
|
|
Ok(v) => {
|
|
let mut updated_user = ActiveModel::from(user);
|
|
match v {
|
|
Update::Username => {
|
|
updated_user.username = Set(Text::new("New username")
|
|
.with_initial_value(updated_user.username.unwrap().as_ref())
|
|
.prompt().unwrap()
|
|
)
|
|
},
|
|
Update::Password => {
|
|
match Password::new("Password")
|
|
.with_validator(min_length!(10))
|
|
.with_display_mode(inquire::PasswordDisplayMode::Masked)
|
|
.prompt() {
|
|
Ok(new_password) => updated_user.hashed_password = Set(hash_password(new_password)),
|
|
Err(_) => println!("Cancelled user update")
|
|
}
|
|
|
|
}
|
|
}
|
|
let _ = updated_user.save(db.as_ref()).await;
|
|
},
|
|
Err(_) => {}
|
|
}
|
|
},
|
|
None => println!("Could not find user")
|
|
}
|
|
}
|
|
}
|
|
},
|
|
Ok(None) | Err(_) => {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn select_user(db: Arc<DatabaseConnection>) -> Option<user::Model> {
|
|
let users = User::find().all(db.as_ref()).await.unwrap();
|
|
if users.is_empty() {
|
|
return None;
|
|
}
|
|
match Select::new("Select a user", users.iter().map(|u| u.username.clone()).collect()).prompt() {
|
|
Ok(selection) => User::find().filter(user::Column::Username.eq(selection)).one(db.as_ref()).await.unwrap(),
|
|
Err(_) => None
|
|
}
|
|
}
|
|
|