feat(admin): create and list users commands
This commit is contained in:
parent
8d20cab18f
commit
a0de3b287b
19 changed files with 314 additions and 30 deletions
|
|
@ -5,9 +5,10 @@ edition = "2021"
|
|||
[dependencies]
|
||||
utils = { path = "../utils" }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
log = { workspace = true }
|
||||
env_logger = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
fully_pub = { workspace = true }
|
||||
strum = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
pub mod users;
|
||||
44
lib/kernel/src/actions/users.rs
Normal file
44
lib/kernel/src/actions/users.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
use crate::{context::KernelContext, models::user::User};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use chrono::SecondsFormat;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CreateUserErr {
|
||||
#[error("Handle or email for user is already used.")]
|
||||
HandleOrEmailNotUnique,
|
||||
|
||||
#[error("Database error.")]
|
||||
DatabaseErr(String)
|
||||
}
|
||||
|
||||
pub async fn create_user(ctx: KernelContext, user: User) -> Result<(), CreateUserErr> {
|
||||
let res = sqlx::query("
|
||||
INSERT INTO users
|
||||
(id, handle, email, status, roles, password_hash, reset_password_token, created_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
")
|
||||
.bind(user.id)
|
||||
.bind(user.handle)
|
||||
.bind(user.email)
|
||||
.bind(user.status.to_string())
|
||||
.bind(user.roles)
|
||||
.bind(user.password_hash)
|
||||
.bind(user.reset_password_token)
|
||||
.bind(user.created_at.to_rfc3339_opts(SecondsFormat::Millis, true))
|
||||
.execute(&ctx.storage.0)
|
||||
.await;
|
||||
match res {
|
||||
Err(err) => {
|
||||
let db_err = &err.as_database_error().unwrap();
|
||||
if db_err.code().unwrap() == "2067" {
|
||||
Err(CreateUserErr::HandleOrEmailNotUnique)
|
||||
} else {
|
||||
dbg!(&err);
|
||||
Err(CreateUserErr::DatabaseErr(db_err.to_string()))
|
||||
}
|
||||
}
|
||||
Ok(_) => Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -29,7 +29,15 @@ struct AppSecrets {
|
|||
jwt_secret: String
|
||||
}
|
||||
|
||||
pub async fn get_kernel_context(start_config: StartKernelConfig) -> Result<(Config, AppSecrets, Storage)> {
|
||||
#[derive(Debug, Clone)]
|
||||
#[fully_pub]
|
||||
struct KernelContext {
|
||||
config: Config,
|
||||
secrets: AppSecrets,
|
||||
storage: Storage
|
||||
}
|
||||
|
||||
pub async fn get_kernel_context(start_config: StartKernelConfig) -> Result<KernelContext> {
|
||||
env_logger::init();
|
||||
let _ = dotenvy::dotenv();
|
||||
|
||||
|
|
@ -47,5 +55,9 @@ pub async fn get_kernel_context(start_config: StartKernelConfig) -> Result<(Conf
|
|||
jwt_secret: env::var("APP_JWT_SECRET").context("Expecting APP_JWT_SECRET env var.")?
|
||||
};
|
||||
|
||||
Ok((config, secrets, storage))
|
||||
Ok(KernelContext {
|
||||
config,
|
||||
secrets,
|
||||
storage
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ use fully_pub::fully_pub;
|
|||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::types::Json;
|
||||
use utils::get_random_human_token;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(sqlx::Type, Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(strum_macros::Display)]
|
||||
|
|
@ -30,3 +32,30 @@ struct User {
|
|||
last_login_at: Option<DateTime<Utc>>,
|
||||
created_at: DateTime<Utc>
|
||||
}
|
||||
|
||||
impl User {
|
||||
pub fn new(
|
||||
handle: String
|
||||
) -> User {
|
||||
User {
|
||||
id: Uuid::new_v4().to_string(),
|
||||
handle,
|
||||
full_name: None,
|
||||
email: None,
|
||||
website: None,
|
||||
picture: None,
|
||||
password_hash: None,
|
||||
status: UserStatus::Disabled,
|
||||
roles: Json(Vec::new()),
|
||||
reset_password_token: None,
|
||||
last_login_at: None,
|
||||
created_at: Utc::now()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invite(self: &mut Self) {
|
||||
self.reset_password_token = Some(get_random_human_token());
|
||||
self.status = UserStatus::Invited;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,17 @@ use crate::models::user::User;
|
|||
use super::storage::Storage;
|
||||
use anyhow::{Result, Context};
|
||||
|
||||
async fn get_user_by_id(storage: &Storage, user_id: &str) -> Result<User> {
|
||||
pub async fn get_user_by_id(storage: &Storage, user_id: &str) -> Result<User> {
|
||||
sqlx::query_as::<_, User>("SELECT * FROM users WHERE id = $1")
|
||||
.bind(user_id)
|
||||
.fetch_one(&storage.0)
|
||||
.await
|
||||
.context("To get user from claim")
|
||||
.context("To get user by id.")
|
||||
}
|
||||
|
||||
pub async fn get_users(storage: &Storage) -> Result<Vec<User>> {
|
||||
sqlx::query_as::<_, User>("SELECT * FROM users")
|
||||
.fetch_all(&storage.0)
|
||||
.await
|
||||
.context("To get users.")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue