use axum::{ Form, extract::{Request, State}, http::{HeaderMap, StatusCode}, response::{IntoResponse, Redirect, Response}, }; use maud::{PreEscaped, html}; use serde::Deserialize; use crate::{ MnemoState, error::CompositeError, logs::{LogAction, LogEntry}, users::{ User, auth::{UserAuthRequired, UserAuthenticate}, handle::UserHandle, permissions::Permission, }, web::{components::nav::nav, icons, pages::base}, }; pub async fn page( State(state): State, req: Request, ) -> Result { let mut conn = state.pool.acquire().await?; let u = match User::authenticate(&mut *conn, req.headers()).await? { Some(u) => u, None => return Ok(Redirect::to(&format!("/login?r={}", req.uri().path())).into_response()), }; let can_create = u .has_permission(&mut *conn, Permission::ManuallyCreateUsers) .await; Ok(base( "Create User | Mnemosyne", html!( (nav(&mut conn, Some(&u), req.uri().path()).await) div class="mx-auto max-w-4xl px-2 my-4" { p class="flex items-center gap-2" { span class="text-neutral-500" {(PreEscaped(icons::USER_PLUS))} span class="text-2xl font-semibold font-lora" {"Create a new user"} } } @if let Ok(true) = can_create { div class="mx-auto max-w-4xl px-2 mt-4" { form action="/users/create-form" method="post" class="flex flex-col" { label for="handle" class="font-light text-neutral-500" {"Handle"} div class="flex w-64 items-center border border-neutral-200/25 rounded bg-neutral-950/50" { span class="pl-2 text-neutral-500 select-none" {"@"} input id="handle" name="handle" type="text" autocomplete="off" class="w-fit pl-0.5 pr-1 py-1 outline-none"; } label for="password" class="font-light text-neutral-500 mt-4" {"Password"} br; input id="password" name="password" type="password" autocomplete="off" class="px-2 w-64 py-1 border border-neutral-200/25 bg-neutral-950/50 rounded"; input type="submit" value="Create" class="px-4 mt-4 w-64 py-1 border border-neutral-200/25 bg-neutral-200/5 rounded cursor-pointer hover:border-neutral-200/40"; } } } @else { p class="text-center p-2" {"You must have permission to view this page."} } ), ) .into_response()) } #[derive(Deserialize)] pub struct CreateUserWithPasswordForm { handle: UserHandle, password: String, } pub async fn create_user( State(state): State, headers: HeaderMap, Form(form): Form, ) -> Result { let mut tx = state.pool.begin().await?; let u = User::authenticate(&mut *tx, &headers).await?.required()?; if !u .has_permission(&mut *tx, Permission::ManuallyCreateUsers) .await? { return Ok((StatusCode::FORBIDDEN).into_response()); } let mut nu = User::create(&mut *tx, form.handle).await?; nu.set_password(&mut *tx, Some(&form.password)).await?; LogEntry::new( &mut *tx, u, LogAction::CreateUser { id: nu.id, handle: nu.handle.as_str().to_string(), }, ) .await?; tx.commit().await?; Ok(Redirect::to("/users").into_response()) }