postgres via sqlx - workable?
This commit is contained in:
@@ -5,13 +5,15 @@ use axum::{
|
||||
routing::get,
|
||||
};
|
||||
|
||||
use crate::MnemoState;
|
||||
|
||||
mod components;
|
||||
mod icons;
|
||||
mod pages;
|
||||
|
||||
pub const CSS: &str = include_str!(concat!(env!("OUT_DIR"), "/styles.css"));
|
||||
|
||||
pub fn web_router() -> Router {
|
||||
pub fn web_router() -> Router<MnemoState> {
|
||||
Router::new()
|
||||
.route(
|
||||
"/styles.css",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use axum::extract::Request;
|
||||
use axum::extract::{Request, State};
|
||||
use chrono::{DateTime, Utc};
|
||||
use maud::{Markup, PreEscaped, html};
|
||||
|
||||
use crate::{
|
||||
database::{self},
|
||||
MnemoState,
|
||||
error::CompositeError,
|
||||
persons::Person,
|
||||
quotes::Quote,
|
||||
@@ -21,19 +21,27 @@ const LINKS: &[(&str, &str, &str)] = &[
|
||||
("Add Person", "/persons", icons::CONTACT),
|
||||
];
|
||||
|
||||
pub async fn page(req: Request) -> Result<Markup, CompositeError> {
|
||||
let u = User::authenticate(req.headers()).ok().flatten();
|
||||
let conn = database::conn()?;
|
||||
pub async fn page(State(state): State<MnemoState>, req: Request) -> Result<Markup, CompositeError> {
|
||||
let mut conn = state.pool.acquire().await?;
|
||||
let u = User::authenticate(&mut *conn, req.headers())
|
||||
.await
|
||||
.ok()
|
||||
.flatten();
|
||||
|
||||
let newest_quote = match u {
|
||||
Some(_) => Quote::get_newest(&conn)?,
|
||||
None => Quote::get_newest_public(&conn)?,
|
||||
Some(_) => Quote::get_newest(&mut *conn).await?,
|
||||
None => Quote::get_newest_public(&mut *conn).await?,
|
||||
};
|
||||
let random_quote = match u {
|
||||
Some(_) => Quote::get_random(&conn)?,
|
||||
Some(_) => Quote::get_random(&mut *conn).await?,
|
||||
None => None,
|
||||
};
|
||||
|
||||
let quote_count = Quote::total_count(&mut *conn).await;
|
||||
let person_count = Person::total_count(&mut *conn).await;
|
||||
let tag_count = Tag::total_count(&mut *conn).await;
|
||||
let user_count = User::total_count(&mut *conn).await;
|
||||
|
||||
Ok(base(
|
||||
"Dashboard | Mnemosyne",
|
||||
html!(
|
||||
@@ -79,25 +87,25 @@ pub async fn page(req: Request) -> Result<Markup, CompositeError> {
|
||||
}
|
||||
div class="mx-auto max-w-4xl px-2 mt-4 flex flex-row gap-2" {
|
||||
(chip(html!({
|
||||
@match Quote::total_count(&conn) {
|
||||
@match quote_count {
|
||||
Ok(count) => {(count) " QUOTES TOTAL"},
|
||||
Err(_) => span class="text-red-400" {"QUOTE COUNT ERR"},
|
||||
}
|
||||
})))
|
||||
(chip(html!({
|
||||
@match Person::total_count(&conn) {
|
||||
@match person_count {
|
||||
Ok(count) => {(count) " PERSONS TOTAL"},
|
||||
Err(_) => span class="text-red-400" {"PERSON COUNT ERR"},
|
||||
}
|
||||
})))
|
||||
(chip(html!({
|
||||
@match Tag::total_count(&conn) {
|
||||
@match tag_count {
|
||||
Ok(count) => {(count) " TAGS TOTAL"},
|
||||
Err(_) => span class="text-red-400" {"TAG COUNT ERR"}
|
||||
}
|
||||
})))
|
||||
(chip(html!({
|
||||
@match User::total_count(&conn) {
|
||||
@match user_count {
|
||||
Ok(count) => {(count) " USERS TOTAL"},
|
||||
Err(_) => span class="text-red-400" {"USER COUNT ERR"}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use axum::{
|
||||
extract::{Query, Request},
|
||||
extract::{Query, Request, State},
|
||||
response::{IntoResponse, Redirect, Response},
|
||||
};
|
||||
use maud::{PreEscaped, html};
|
||||
@@ -8,10 +8,8 @@ use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
config::REFERENCE_SPLASHES,
|
||||
users::{
|
||||
User,
|
||||
auth::{AuthError, UserAuthenticate},
|
||||
},
|
||||
error::CompositeError,
|
||||
users::{User, auth::UserAuthenticate},
|
||||
web::{components::marquee::marquee, icons, pages::base},
|
||||
};
|
||||
|
||||
@@ -20,8 +18,13 @@ pub struct LoginMsg {
|
||||
msg: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn page(Query(q): Query<LoginMsg>, req: Request) -> Result<Response, AuthError> {
|
||||
let u = User::authenticate(req.headers())?;
|
||||
pub async fn page(
|
||||
State(state): State<crate::MnemoState>,
|
||||
Query(q): Query<LoginMsg>,
|
||||
req: Request,
|
||||
) -> Result<Response, CompositeError> {
|
||||
let mut conn = state.pool.acquire().await?;
|
||||
let u = User::authenticate(&mut *conn, req.headers()).await?;
|
||||
if u.is_some() {
|
||||
return Ok(Redirect::to("/dashboard").into_response());
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use axum::{
|
||||
extract::{Query, Request},
|
||||
extract::{Query, Request, State},
|
||||
response::{IntoResponse, Redirect, Response},
|
||||
};
|
||||
use maud::{PreEscaped, html};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
database::{self},
|
||||
MnemoState,
|
||||
error::CompositeError,
|
||||
logs::LogEntry,
|
||||
users::{User, auth::UserAuthenticate},
|
||||
@@ -19,22 +19,22 @@ pub struct PageQuery {
|
||||
}
|
||||
|
||||
pub async fn page(
|
||||
State(state): State<MnemoState>,
|
||||
Query(query): Query<PageQuery>,
|
||||
req: Request,
|
||||
) -> Result<Response, CompositeError> {
|
||||
let u = match User::authenticate(req.headers())? {
|
||||
let mut tx = state.pool.begin().await?;
|
||||
let u = match User::authenticate(&mut *tx, req.headers()).await? {
|
||||
Some(u) => u,
|
||||
None => return Ok(Redirect::to(&format!("/login?r={}", req.uri().path())).into_response()),
|
||||
};
|
||||
let mut conn = database::conn()?;
|
||||
let tx = conn.transaction()?;
|
||||
|
||||
let page = query.page.unwrap_or(1).max(1);
|
||||
let per_page = 20;
|
||||
let offset = (page - 1) * per_page;
|
||||
|
||||
let logs = LogEntry::get_chronological_offset(&tx, offset, per_page)?;
|
||||
let total_logs = LogEntry::total_count(&tx)?;
|
||||
let logs = LogEntry::get_chronological_offset(&mut *tx, offset, per_page).await?;
|
||||
let total_logs = LogEntry::total_count(&mut *tx).await?;
|
||||
let total_pages = (total_logs as f64 / per_page as f64).ceil() as i64;
|
||||
|
||||
Ok(base(
|
||||
@@ -42,7 +42,7 @@ pub async fn page(
|
||||
html!(
|
||||
(nav(Some(&u), req.uri().path()))
|
||||
|
||||
@if true {//let Ok(true) = u.has_permission(&tx, Permission::BrowseServerLogs) {
|
||||
@if true {//let Ok(true) = u.has_permission(&mut *tx, Permission::BrowseServerLogs) {
|
||||
div class="max-w-4xl mx-auto px-2" {
|
||||
div class="my-4" {
|
||||
p class="flex items-center gap-2" {
|
||||
|
||||
@@ -4,6 +4,8 @@ use axum::{
|
||||
};
|
||||
use maud::{DOCTYPE, Markup, html};
|
||||
|
||||
use crate::MnemoState;
|
||||
|
||||
pub mod dashboard;
|
||||
pub mod index;
|
||||
pub mod login;
|
||||
@@ -15,7 +17,7 @@ pub mod tags;
|
||||
pub mod users;
|
||||
pub mod usersettings;
|
||||
|
||||
pub fn pages() -> Router {
|
||||
pub fn pages() -> Router<MnemoState> {
|
||||
Router::new()
|
||||
.route("/", get(index::page))
|
||||
.route("/login", get(login::page))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use axum::extract::Request;
|
||||
use axum::extract::{Request, State};
|
||||
use maud::{Markup, html};
|
||||
|
||||
use crate::{
|
||||
@@ -7,8 +7,15 @@ use crate::{
|
||||
web::{components::nav::nav, pages::base},
|
||||
};
|
||||
|
||||
pub async fn page(req: Request) -> Result<Markup, CompositeError> {
|
||||
let u = User::authenticate(req.headers()).ok().flatten();
|
||||
pub async fn page(
|
||||
State(state): State<crate::MnemoState>,
|
||||
req: Request,
|
||||
) -> Result<Markup, CompositeError> {
|
||||
let mut conn = state.pool.acquire().await?;
|
||||
let u = User::authenticate(&mut *conn, req.headers())
|
||||
.await
|
||||
.ok()
|
||||
.flatten();
|
||||
Ok(base(
|
||||
"Not Found | Mnemosyne",
|
||||
html!(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use axum::{
|
||||
Form,
|
||||
extract::Request,
|
||||
extract::State,
|
||||
http::HeaderMap,
|
||||
response::{IntoResponse, Redirect, Response},
|
||||
};
|
||||
@@ -8,26 +9,37 @@ use maud::{PreEscaped, html};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
database::{self},
|
||||
error::CompositeError,
|
||||
logs::{LogAction, LogEntry},
|
||||
persons::Person,
|
||||
users::{
|
||||
User,
|
||||
auth::{AuthError, UserAuthRequired, UserAuthenticate},
|
||||
auth::{UserAuthRequired, UserAuthenticate},
|
||||
},
|
||||
web::{components::nav::nav, icons, pages::base},
|
||||
};
|
||||
|
||||
pub mod profile;
|
||||
|
||||
pub async fn page(req: Request) -> Result<Response, AuthError> {
|
||||
let u = match User::authenticate(req.headers())? {
|
||||
pub async fn page(
|
||||
State(state): State<crate::MnemoState>,
|
||||
req: Request,
|
||||
) -> Result<Response, CompositeError> {
|
||||
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 mut conn = database::conn()?;
|
||||
let tx = conn.transaction()?;
|
||||
|
||||
let total_count = Person::total_count(&mut *conn).await;
|
||||
let persons_res = Person::get_all(&mut *conn).await;
|
||||
|
||||
let mut person_counts = vec![];
|
||||
if let Ok(ref persons) = persons_res {
|
||||
for p in persons {
|
||||
person_counts.push(p.get_in_quote_count(&mut *conn).await);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(base(
|
||||
"Persons | Mnemosyne",
|
||||
@@ -40,30 +52,28 @@ pub async fn page(req: Request) -> Result<Response, AuthError> {
|
||||
span class="text-2xl font-semibold font-lora" {"Persons"}
|
||||
}
|
||||
p class="text-neutral-500 text-sm font-light" {
|
||||
@if let Ok(c) = Person::total_count(&tx) {
|
||||
@if let Ok(c) = total_count {
|
||||
(c) " persons in total."
|
||||
} @else {
|
||||
"Could not get total person count."
|
||||
}
|
||||
}
|
||||
}
|
||||
@if let Ok(persons) = Person::get_all(&tx) {
|
||||
@if let Ok(persons) = persons_res {
|
||||
div class="max-w-4xl mx-auto px-2 mt-4 flex flex-wrap gap-2" {
|
||||
@for person in &persons {
|
||||
@for (idx, person) in persons.iter().enumerate() {
|
||||
a href={"/persons/"(person.id)} class="rounded px-4 py-2 bg-neutral-200/5 hover:bg-neutral-200/10 border border-neutral-200/25 hover:border-neutral-200/25 flex items-center" {
|
||||
span class="text-neutral-400 mr-1 scale-125" {"~"}
|
||||
span class="text-sm" {(person.primary_name)}
|
||||
div class="w-px h-2/3 my-auto mx-2 bg-neutral-200/15" {}
|
||||
div class="text-xs flex items-center" {
|
||||
(
|
||||
if let Ok(i) = person.get_in_quote_count(&tx) {
|
||||
if let Ok(i) = person_counts[idx] {
|
||||
i.to_string()
|
||||
} else {
|
||||
"?".to_string()
|
||||
}
|
||||
) span class="*:size-3 ml-1 text-neutral-400" {(PreEscaped(icons::SCROLL_TEXT))}
|
||||
// div class="ml-2" {}
|
||||
// "4" span class="*:size-3 ml-1 text-neutral-400" {(PreEscaped(icons::FILE_IMAGE))}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,22 +106,23 @@ pub struct PersonNameForm {
|
||||
primary_name: String,
|
||||
}
|
||||
pub async fn create(
|
||||
State(state): State<crate::MnemoState>,
|
||||
headers: HeaderMap,
|
||||
Form(form): Form<PersonNameForm>,
|
||||
) -> Result<Response, CompositeError> {
|
||||
let u = User::authenticate(&headers)?.required()?;
|
||||
let mut conn = database::conn()?;
|
||||
let tx = conn.transaction()?;
|
||||
let mut tx = state.pool.begin().await?;
|
||||
let u = User::authenticate(&mut *tx, &headers).await?.required()?;
|
||||
|
||||
let p = Person::create(&tx, form.primary_name, u.id)?;
|
||||
let p = Person::create(&mut *tx, form.primary_name, u.id).await?;
|
||||
LogEntry::new(
|
||||
&tx,
|
||||
&mut *tx,
|
||||
u,
|
||||
LogAction::CreatePerson {
|
||||
id: p.id,
|
||||
pname: p.primary_name,
|
||||
},
|
||||
)?;
|
||||
tx.commit()?;
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
Ok(Redirect::to("/persons").into_response())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use axum::{
|
||||
Form,
|
||||
extract::{Path, Request},
|
||||
extract::{Path, Request, State},
|
||||
http::HeaderMap,
|
||||
response::{IntoResponse, Redirect, Response},
|
||||
};
|
||||
@@ -9,29 +9,44 @@ use serde::Deserialize;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
database,
|
||||
error::CompositeError,
|
||||
logs::{LogAction, LogEntry},
|
||||
persons::{Name, Person},
|
||||
users::{
|
||||
User,
|
||||
auth::{AuthError, UserAuthRequired, UserAuthenticate},
|
||||
auth::{UserAuthRequired, UserAuthenticate},
|
||||
},
|
||||
web::{components::nav::nav, pages::base},
|
||||
};
|
||||
|
||||
pub async fn page(Path(id): Path<Uuid>, req: Request) -> Result<Response, AuthError> {
|
||||
let u = match User::authenticate(req.headers())? {
|
||||
pub async fn page(
|
||||
State(state): State<crate::MnemoState>,
|
||||
Path(id): Path<Uuid>,
|
||||
req: Request,
|
||||
) -> Result<Response, CompositeError> {
|
||||
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 conn = database::conn()?;
|
||||
let p = Person::get_by_id(&conn, id);
|
||||
let p = Person::get_by_id(&mut *conn, id).await;
|
||||
let title = match &p {
|
||||
Ok(p) => format!("~{} | Mnemosyne", p.primary_name),
|
||||
Err(_) => "Error! | Mnemosyne".into(),
|
||||
};
|
||||
|
||||
let mut names_with_attribution = Vec::new();
|
||||
let mut names_ok = false;
|
||||
if let Ok(ref person) = p {
|
||||
if let Ok(names) = person.get_all_names(&mut *conn).await {
|
||||
names_ok = true;
|
||||
for name in names {
|
||||
let attr = name.times_attributed(&mut *conn).await.unwrap_or(0);
|
||||
names_with_attribution.push((name, attr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(base(
|
||||
&title,
|
||||
html!(
|
||||
@@ -46,14 +61,14 @@ pub async fn page(Path(id): Path<Uuid>, req: Request) -> Result<Response, AuthEr
|
||||
div {
|
||||
h2 class="text-lg font-semibold font-lora mb-2 text-neutral-300" {"Names"}
|
||||
div class="flex flex-wrap gap-2 mb-4" {
|
||||
@if let Ok(names) = p.get_all_names(&conn) {
|
||||
@for name in &names {
|
||||
@if names_ok {
|
||||
@for (name, attr) in names_with_attribution {
|
||||
div class="rounded px-3 py-1 bg-neutral-200/5 border border-neutral-200/10 text-sm flex items-center gap-2" {
|
||||
(name.name)
|
||||
@if name.is_primary {
|
||||
span class="text-xs text-neutral-500" {"(primary)"}
|
||||
}
|
||||
@if let Ok(0) = name.times_attributed(&conn) && !name.is_primary {
|
||||
@if attr == 0 && !name.is_primary {
|
||||
form action=(format!("/names/{}/delete", name.id)) method="post" class="flex items-center ml-1" {
|
||||
button type="submit" class="text-neutral-500 hover:text-red-400 flex items-center justify-center cursor-pointer" title="Delete" {
|
||||
"✕"
|
||||
@@ -91,19 +106,19 @@ pub struct AddNameForm {
|
||||
}
|
||||
|
||||
pub async fn add_name(
|
||||
State(state): State<crate::MnemoState>,
|
||||
Path(id): Path<Uuid>,
|
||||
headers: HeaderMap,
|
||||
Form(form): Form<AddNameForm>,
|
||||
) -> Result<Response, CompositeError> {
|
||||
let u = User::authenticate(&headers)?.required()?;
|
||||
let mut conn = database::conn()?;
|
||||
let tx = conn.transaction()?;
|
||||
let mut tx = state.pool.begin().await?;
|
||||
let u = User::authenticate(&mut *tx, &headers).await?.required()?;
|
||||
|
||||
let p = Person::get_by_id(&tx, id)?;
|
||||
let n = p.add_name(&tx, form.name, u.id)?;
|
||||
let p = Person::get_by_id(&mut *tx, id).await?;
|
||||
let n = p.add_name(&mut *tx, form.name, u.id).await?;
|
||||
|
||||
LogEntry::new(
|
||||
&tx,
|
||||
&mut *tx,
|
||||
u,
|
||||
LogAction::AddPersonName {
|
||||
pid: p.id,
|
||||
@@ -111,28 +126,29 @@ pub async fn add_name(
|
||||
pn: p.primary_name,
|
||||
nn: n.name,
|
||||
},
|
||||
)?;
|
||||
tx.commit()?;
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(Redirect::to(&format!("/persons/{}", p.id)).into_response())
|
||||
}
|
||||
|
||||
pub async fn delete_name(
|
||||
State(state): State<crate::MnemoState>,
|
||||
Path(id): Path<Uuid>,
|
||||
headers: HeaderMap,
|
||||
) -> Result<Response, CompositeError> {
|
||||
let u = User::authenticate(&headers)?.required()?;
|
||||
let mut conn = database::conn()?;
|
||||
let tx = conn.transaction()?;
|
||||
let mut tx = state.pool.begin().await?;
|
||||
let u = User::authenticate(&mut *tx, &headers).await?.required()?;
|
||||
|
||||
let n = Name::get_by_id(&tx, id)?;
|
||||
let p = Person::get_by_id(&tx, n.person_id)?;
|
||||
let n = Name::get_by_id(&mut *tx, id).await?;
|
||||
let p = Person::get_by_id(&mut *tx, n.person_id).await?;
|
||||
|
||||
let nn = n.name.clone();
|
||||
n.delete(&tx)?;
|
||||
n.delete(&mut *tx).await?;
|
||||
|
||||
LogEntry::new(
|
||||
&tx,
|
||||
&mut *tx,
|
||||
u,
|
||||
LogAction::DeletePersonName {
|
||||
pid: p.id,
|
||||
@@ -140,8 +156,9 @@ pub async fn delete_name(
|
||||
pn: p.primary_name,
|
||||
n: nn,
|
||||
},
|
||||
)?;
|
||||
tx.commit()?;
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(Redirect::to(&format!("/persons/{}", p.id)).into_response())
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ use maud::{PreEscaped, html};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
database,
|
||||
error::CompositeError,
|
||||
quotes::Quote,
|
||||
users::{User, auth::UserAuthenticate},
|
||||
@@ -25,21 +24,22 @@ pub struct PageQuery {
|
||||
}
|
||||
|
||||
pub async fn page(
|
||||
axum::extract::State(state): axum::extract::State<crate::MnemoState>,
|
||||
Query(query): Query<PageQuery>,
|
||||
req: Request,
|
||||
) -> Result<Response, CompositeError> {
|
||||
let u = match User::authenticate(req.headers())? {
|
||||
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 conn = database::conn()?;
|
||||
|
||||
let page = query.page.unwrap_or(1).max(1);
|
||||
let per_page = 10;
|
||||
let offset = (page - 1) * per_page;
|
||||
|
||||
let quotes = Quote::get_chronological_offset(&conn, offset, per_page)?;
|
||||
let total_quotes = Quote::total_count(&conn)?;
|
||||
let quotes = Quote::get_chronological_offset(&mut *conn, offset, per_page).await?;
|
||||
let total_quotes = Quote::total_count(&mut *conn).await?;
|
||||
let total_pages = (total_quotes as f64 / per_page as f64).ceil() as i64;
|
||||
|
||||
Ok(base(
|
||||
|
||||
@@ -4,14 +4,12 @@ use axum::{
|
||||
response::{IntoResponse, Redirect, Response},
|
||||
};
|
||||
use axum_extra::extract::Form;
|
||||
use chrono::{TimeZone, Utc};
|
||||
use chrono_tz::Europe::Warsaw;
|
||||
use chrono::NaiveDateTime;
|
||||
use maud::{Markup, PreEscaped, html};
|
||||
use serde::Deserialize;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
database,
|
||||
error::CompositeError,
|
||||
logs::{LogAction, LogEntry},
|
||||
persons::Name,
|
||||
@@ -26,13 +24,16 @@ use crate::{
|
||||
const LINE_ADD_RM_SCRIPT: &str = include_str!("line-add-rm.js");
|
||||
const PREFILL_TIME_SCRIPT: &str = include_str!("prefill-time.js");
|
||||
|
||||
pub async fn page(req: Request) -> Result<Response, CompositeError> {
|
||||
let u = match User::authenticate(req.headers())? {
|
||||
pub async fn page(
|
||||
axum::extract::State(state): axum::extract::State<crate::MnemoState>,
|
||||
req: Request,
|
||||
) -> Result<Response, CompositeError> {
|
||||
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 conn = database::conn()?;
|
||||
let names = Name::get_all(&conn)?;
|
||||
let names = Name::get_all(&mut *conn).await?;
|
||||
|
||||
Ok(base(
|
||||
"Add Quote | Mnemosyne",
|
||||
@@ -137,30 +138,27 @@ pub struct IncomingQuote {
|
||||
authors: Vec<Uuid>,
|
||||
location: String,
|
||||
time: String,
|
||||
tz_offset: Option<i32>,
|
||||
context: String,
|
||||
}
|
||||
pub async fn form(
|
||||
axum::extract::State(state): axum::extract::State<crate::MnemoState>,
|
||||
headers: HeaderMap,
|
||||
Form(form): Form<IncomingQuote>,
|
||||
) -> Result<Response, CompositeError> {
|
||||
let u = User::authenticate(&headers)?.required()?;
|
||||
let mut conn = database::conn()?;
|
||||
let tx = conn.transaction()?;
|
||||
let mut tx = state.pool.begin().await?;
|
||||
let u = User::authenticate(&mut *tx, &headers).await?.required()?;
|
||||
|
||||
let authors = form
|
||||
.authors
|
||||
.into_iter()
|
||||
.map(|nid| Name::get_by_id(&tx, nid).unwrap());
|
||||
let mut authors = Vec::new();
|
||||
for nid in form.authors {
|
||||
authors.push(Name::get_by_id(&mut *tx, nid).await.unwrap());
|
||||
}
|
||||
let lines = form.lines.into_iter().zip(authors).collect();
|
||||
let offset = form
|
||||
.tz_offset
|
||||
.and_then(|mins| chrono::FixedOffset::west_opt(mins * 60))
|
||||
.unwrap_or_else(|| chrono::FixedOffset::west_opt(0).unwrap());
|
||||
|
||||
let timestamp = chrono::NaiveDateTime::parse_from_str(&form.time, "%Y-%m-%dT%H:%M")
|
||||
.map(|ndt| offset.from_local_datetime(&ndt).unwrap())
|
||||
.unwrap_or_else(|_| Utc::now().with_timezone(&Warsaw).fixed_offset());
|
||||
let timestamp = match NaiveDateTime::parse_from_str(&form.time, "%Y-%m-%dT%H:%M") {
|
||||
Ok(ts) => ts,
|
||||
Err(_) => return Ok("Time was formatted wrong.".into_response()),
|
||||
};
|
||||
|
||||
let context = match form.context.trim() {
|
||||
"" => None,
|
||||
s => Some(s.to_string()),
|
||||
@@ -170,9 +168,9 @@ pub async fn form(
|
||||
s => Some(s.to_string()),
|
||||
};
|
||||
|
||||
let q = Quote::create(&tx, lines, timestamp, context, location, u.id, false)?;
|
||||
LogEntry::new(&tx, u, LogAction::CreateQuote { id: q.id })?;
|
||||
tx.commit()?;
|
||||
let q = Quote::create(&mut *tx, lines, timestamp, context, location, u.id, false).await?;
|
||||
LogEntry::new(&mut *tx, u, LogAction::CreateQuote { id: q.id }).await?;
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(Redirect::to("/dashboard").into_response())
|
||||
}
|
||||
|
||||
@@ -9,23 +9,38 @@ use serde::Deserialize;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
database::{self},
|
||||
error::CompositeError,
|
||||
logs::{LogAction, LogEntry},
|
||||
tags::{Tag, TagName},
|
||||
users::{
|
||||
User,
|
||||
auth::{AuthError, UserAuthRequired, UserAuthenticate},
|
||||
auth::{UserAuthRequired, UserAuthenticate},
|
||||
},
|
||||
web::{components::nav::nav, icons, pages::base},
|
||||
};
|
||||
|
||||
pub async fn page(req: Request) -> Result<Response, AuthError> {
|
||||
let u = match User::authenticate(req.headers())? {
|
||||
pub async fn page(
|
||||
axum::extract::State(state): axum::extract::State<crate::MnemoState>,
|
||||
req: Request,
|
||||
) -> Result<Response, CompositeError> {
|
||||
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 conn = database::conn()?;
|
||||
let total_tags = Tag::total_count(&mut *conn).await;
|
||||
let mut tags_with_counts = Vec::new();
|
||||
let tags = Tag::get_all(&mut *conn).await;
|
||||
let mut is_tags_ok = false;
|
||||
let mut is_tags_empty = true;
|
||||
if let Ok(ts) = tags {
|
||||
is_tags_ok = true;
|
||||
is_tags_empty = ts.is_empty();
|
||||
for tag in ts {
|
||||
let count = tag.get_tagged_quotes_count(&mut *conn).await;
|
||||
tags_with_counts.push((tag, count));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(base(
|
||||
"Tags | Mnemosyne",
|
||||
@@ -38,23 +53,23 @@ pub async fn page(req: Request) -> Result<Response, AuthError> {
|
||||
span class="text-2xl font-semibold font-lora" {"Tags"}
|
||||
}
|
||||
p class="text-neutral-500 text-sm font-light" {
|
||||
@if let Ok(c) = Tag::total_count(&conn) {
|
||||
@if let Ok(c) = total_tags {
|
||||
(c) " tags in total."
|
||||
} @else {
|
||||
"Could not get total tag count."
|
||||
}
|
||||
}
|
||||
}
|
||||
@if let Ok(tags) = Tag::get_all(&conn) {
|
||||
@if is_tags_ok {
|
||||
div class="max-w-4xl mx-auto px-2 mt-4 flex flex-wrap gap-2" {
|
||||
@for tag in &tags {
|
||||
@for (tag, count) in tags_with_counts {
|
||||
div class="rounded-full px-3 py-1 bg-neutral-200/10 border border-neutral-200/15 flex" {
|
||||
span class="text-neutral-400 text-sm" {"#"}
|
||||
span class="text-sm" {(tag.name)}
|
||||
div class="w-px h-2/3 my-auto mx-2 bg-neutral-200/15" {}
|
||||
div class="text-xs flex items-center" {
|
||||
(
|
||||
if let Ok(i) = tag.get_tagged_quotes_count(&conn) {
|
||||
if let Ok(i) = &count {
|
||||
i.to_string()
|
||||
} else {
|
||||
"?".to_string()
|
||||
@@ -63,7 +78,7 @@ pub async fn page(req: Request) -> Result<Response, AuthError> {
|
||||
// div class="ml-2" {}
|
||||
// "0" span class="*:size-3 ml-1 text-neutral-400" {(PreEscaped(icons::FILE_IMAGE))}
|
||||
}
|
||||
@if let Ok(0) = tag.get_tagged_quotes_count(&conn) {
|
||||
@if let Ok(0) = count {
|
||||
form action=(format!("/tags/{}/delete", tag.id)) method="post" class="flex items-center ml-1" {
|
||||
button type="submit" class="text-neutral-500 hover:text-red-400 text-sm flex items-center justify-center cursor-pointer" title="Delete" {
|
||||
"✕"
|
||||
@@ -73,7 +88,7 @@ pub async fn page(req: Request) -> Result<Response, AuthError> {
|
||||
}
|
||||
}
|
||||
}
|
||||
@if tags.is_empty() {
|
||||
@if is_tags_empty {
|
||||
p class="text-center p-2" {"No tags yet. How about making one?"}
|
||||
}
|
||||
div class="mx-auto max-w-4xl mt-4 px-2" {
|
||||
@@ -101,40 +116,41 @@ pub struct TagForm {
|
||||
tagname: TagName,
|
||||
}
|
||||
pub async fn create(
|
||||
axum::extract::State(state): axum::extract::State<crate::MnemoState>,
|
||||
headers: HeaderMap,
|
||||
Form(form): Form<TagForm>,
|
||||
) -> Result<Response, CompositeError> {
|
||||
let u = User::authenticate(&headers)?.required()?;
|
||||
let mut conn = database::conn()?;
|
||||
let tx = conn.transaction()?;
|
||||
let mut tx = state.pool.begin().await?;
|
||||
let u = User::authenticate(&mut *tx, &headers).await?.required()?;
|
||||
|
||||
let t = Tag::create(&tx, form.tagname)?;
|
||||
let t = Tag::create(&mut *tx, form.tagname).await?;
|
||||
LogEntry::new(
|
||||
&tx,
|
||||
&mut *tx,
|
||||
u,
|
||||
LogAction::CreateTag {
|
||||
id: t.id,
|
||||
name: t.name.to_string(),
|
||||
},
|
||||
)?;
|
||||
tx.commit()?;
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
Ok(Redirect::to("/tags").into_response())
|
||||
}
|
||||
|
||||
pub async fn delete_tag(
|
||||
axum::extract::State(state): axum::extract::State<crate::MnemoState>,
|
||||
Path(id): Path<Uuid>,
|
||||
headers: HeaderMap,
|
||||
) -> Result<Response, CompositeError> {
|
||||
let u = User::authenticate(&headers)?.required()?;
|
||||
let mut conn = database::conn()?;
|
||||
let tx = conn.transaction()?;
|
||||
let mut tx = state.pool.begin().await?;
|
||||
let u = User::authenticate(&mut *tx, &headers).await?.required()?;
|
||||
|
||||
let t = Tag::get_by_id(&tx, id)?;
|
||||
let t = Tag::get_by_id(&mut *tx, id).await?;
|
||||
let name = t.name.as_str().to_string();
|
||||
t.delete(&tx)?;
|
||||
t.delete(&mut *tx).await?;
|
||||
|
||||
LogEntry::new(&tx, u, LogAction::DeleteTag { id, name })?;
|
||||
tx.commit()?;
|
||||
LogEntry::new(&mut *tx, u, LogAction::DeleteTag { id, name }).await?;
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(Redirect::to("/tags").into_response())
|
||||
}
|
||||
|
||||
@@ -5,12 +5,8 @@ use axum::{
|
||||
use maud::{PreEscaped, html};
|
||||
|
||||
use crate::{
|
||||
database,
|
||||
users::{
|
||||
User,
|
||||
auth::{AuthError, UserAuthenticate},
|
||||
permissions::Permission,
|
||||
},
|
||||
error::CompositeError,
|
||||
users::{User, auth::UserAuthenticate, permissions::Permission},
|
||||
web::{
|
||||
components::{nav::nav, user_miniprofile::user_miniprofile},
|
||||
icons,
|
||||
@@ -21,13 +17,19 @@ use crate::{
|
||||
pub mod create;
|
||||
pub mod profile;
|
||||
|
||||
pub async fn page(req: Request) -> Result<Response, AuthError> {
|
||||
let u = match User::authenticate(req.headers())? {
|
||||
pub async fn page(
|
||||
axum::extract::State(state): axum::extract::State<crate::MnemoState>,
|
||||
req: Request,
|
||||
) -> Result<Response, CompositeError> {
|
||||
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 conn = database::conn()?;
|
||||
let us = User::get_all(&conn);
|
||||
let us = User::get_all(&mut *conn).await;
|
||||
let can_create_users = u
|
||||
.has_permission(&mut *conn, Permission::ManuallyCreateUsers)
|
||||
.await;
|
||||
|
||||
Ok(base(
|
||||
"Users | Mnemosyne",
|
||||
@@ -45,7 +47,7 @@ pub async fn page(req: Request) -> Result<Response, AuthError> {
|
||||
} @else {
|
||||
"Could not fetch user count."
|
||||
}
|
||||
@if let Ok(true) = u.has_permission(&conn, Permission::ManuallyCreateUsers) {
|
||||
@if let Ok(true) = can_create_users {
|
||||
" "
|
||||
a href="/users/create" class="text-blue-500 hover:text-blue-400 hover:underline" {
|
||||
"Create a new user"
|
||||
|
||||
@@ -8,24 +8,29 @@ use maud::{PreEscaped, html};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
database::{self},
|
||||
error::CompositeError,
|
||||
logs::{LogAction, LogEntry},
|
||||
users::{
|
||||
User,
|
||||
auth::{AuthError, UserAuthRequired, UserAuthenticate},
|
||||
auth::{UserAuthRequired, UserAuthenticate},
|
||||
handle::UserHandle,
|
||||
permissions::Permission,
|
||||
},
|
||||
web::{components::nav::nav, icons, pages::base},
|
||||
};
|
||||
|
||||
pub async fn page(req: Request) -> Result<Response, AuthError> {
|
||||
let u = match User::authenticate(req.headers())? {
|
||||
pub async fn page(
|
||||
axum::extract::State(state): axum::extract::State<crate::MnemoState>,
|
||||
req: Request,
|
||||
) -> Result<Response, CompositeError> {
|
||||
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 conn = database::conn()?;
|
||||
let can_create = u
|
||||
.has_permission(&mut *conn, Permission::ManuallyCreateUsers)
|
||||
.await;
|
||||
|
||||
Ok(base(
|
||||
"Create User | Mnemosyne",
|
||||
@@ -38,7 +43,7 @@ pub async fn page(req: Request) -> Result<Response, AuthError> {
|
||||
span class="text-2xl font-semibold font-lora" {"Create a new user"}
|
||||
}
|
||||
}
|
||||
@if let Ok(true) = u.has_permission(&conn, Permission::ManuallyCreateUsers) {
|
||||
@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"}
|
||||
@@ -68,26 +73,30 @@ pub struct CreateUserWithPasswordForm {
|
||||
password: String,
|
||||
}
|
||||
pub async fn create_user(
|
||||
axum::extract::State(state): axum::extract::State<crate::MnemoState>,
|
||||
headers: HeaderMap,
|
||||
Form(form): Form<CreateUserWithPasswordForm>,
|
||||
) -> Result<Response, CompositeError> {
|
||||
let u = User::authenticate(&headers)?.required()?;
|
||||
let mut conn = database::conn()?;
|
||||
let tx = conn.transaction()?;
|
||||
let mut tx = state.pool.begin().await?;
|
||||
let u = User::authenticate(&mut *tx, &headers).await?.required()?;
|
||||
|
||||
if !u.has_permission(&tx, Permission::ManuallyCreateUsers)? {
|
||||
if !u
|
||||
.has_permission(&mut *tx, Permission::ManuallyCreateUsers)
|
||||
.await?
|
||||
{
|
||||
return Ok((StatusCode::FORBIDDEN).into_response());
|
||||
}
|
||||
let mut nu = User::create(&tx, form.handle)?;
|
||||
nu.set_password(&tx, Some(&form.password))?;
|
||||
let mut nu = User::create(&mut *tx, form.handle).await?;
|
||||
nu.set_password(&mut *tx, Some(&form.password)).await?;
|
||||
LogEntry::new(
|
||||
&tx,
|
||||
&mut *tx,
|
||||
u,
|
||||
LogAction::CreateUser {
|
||||
id: nu.id,
|
||||
handle: nu.handle.as_str().to_string(),
|
||||
},
|
||||
)?;
|
||||
tx.commit()?;
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
Ok(Redirect::to("/users").into_response())
|
||||
}
|
||||
|
||||
@@ -2,15 +2,11 @@ use axum::{
|
||||
extract::{Path, Request},
|
||||
response::{IntoResponse, Redirect, Response},
|
||||
};
|
||||
use chrono::{DateTime, Utc};
|
||||
use maud::{PreEscaped, html};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
database::{self},
|
||||
error::CompositeError,
|
||||
persons::Name,
|
||||
quotes::{Quote, QuoteLine},
|
||||
users::{User, UserError, auth::UserAuthenticate},
|
||||
web::{
|
||||
components::{nav::nav, quote::quote},
|
||||
@@ -19,15 +15,18 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
pub async fn page(Path(id): Path<Uuid>, req: Request) -> Result<Response, CompositeError> {
|
||||
let u = match User::authenticate(req.headers())? {
|
||||
pub async fn page(
|
||||
axum::extract::State(state): axum::extract::State<crate::MnemoState>,
|
||||
Path(id): Path<Uuid>,
|
||||
req: Request,
|
||||
) -> Result<Response, CompositeError> {
|
||||
let mut tx = state.pool.begin().await?;
|
||||
let u = match User::authenticate(&mut *tx, req.headers()).await? {
|
||||
Some(u) => u,
|
||||
None => return Ok(Redirect::to(&format!("/login?r={}", req.uri().path())).into_response()),
|
||||
};
|
||||
let mut conn = database::conn()?;
|
||||
let tx = conn.transaction()?;
|
||||
|
||||
let user = match User::get_by_id(&tx, id) {
|
||||
let user = match User::get_by_id(&mut *tx, id).await {
|
||||
Ok(u) => u,
|
||||
Err(UserError::NoUserWithId(_)) => {
|
||||
return Ok(base(
|
||||
@@ -65,7 +64,7 @@ pub async fn page(Path(id): Path<Uuid>, req: Request) -> Result<Response, Compos
|
||||
.to_uppercase()
|
||||
.to_string();
|
||||
let joined_str = user.created_at().map(|d| d.format("%Y-%m-%d").to_string());
|
||||
let sample_quotes = sample_quotes_for_display();
|
||||
let sample_quotes = vec![];
|
||||
|
||||
Ok(base(
|
||||
&format!("@{} | Mnemosyne", user.handle),
|
||||
@@ -199,72 +198,3 @@ pub async fn page(Path(id): Path<Uuid>, req: Request) -> Result<Response, Compos
|
||||
)
|
||||
.into_response())
|
||||
}
|
||||
|
||||
fn sample_quotes_for_display() -> Vec<Quote> {
|
||||
vec![
|
||||
Quote {
|
||||
id: Uuid::now_v7(),
|
||||
public: true,
|
||||
location: Some(String::from("Poznań")),
|
||||
context: Some(String::from("Wykład z językoznawstwa")),
|
||||
created_by: Uuid::max(),
|
||||
timestamp: DateTime::from(Utc::now()),
|
||||
lines: vec![
|
||||
QuoteLine {
|
||||
id: Uuid::now_v7(),
|
||||
content: String::from("Nie wiem, czy są tutaj osoby fanowskie zipline-ów?"),
|
||||
attribution: Name {
|
||||
id: Uuid::nil(),
|
||||
created_by: Uuid::max(),
|
||||
person_id: Uuid::now_v7(),
|
||||
is_primary: true,
|
||||
name: String::from("dr. Barbara Konat"),
|
||||
},
|
||||
},
|
||||
QuoteLine {
|
||||
id: Uuid::now_v7(),
|
||||
content: String::from("Taka uprząż co robi pziuuum!"),
|
||||
attribution: Name {
|
||||
id: Uuid::nil(),
|
||||
created_by: Uuid::max(),
|
||||
person_id: Uuid::now_v7(),
|
||||
is_primary: true,
|
||||
name: String::from("dr. Barbara Konat"),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
Quote {
|
||||
id: Uuid::now_v7(),
|
||||
public: true,
|
||||
location: Some(String::from("Discord VC")),
|
||||
context: Some(String::from("O narysowanej dziewczynie")),
|
||||
created_by: Uuid::max(),
|
||||
timestamp: DateTime::from(Utc::now()),
|
||||
lines: vec![
|
||||
QuoteLine {
|
||||
id: Uuid::now_v7(),
|
||||
content: String::from("Czy tu proporcje są zachowane?"),
|
||||
attribution: Name {
|
||||
id: Uuid::now_v7(),
|
||||
created_by: Uuid::max(),
|
||||
person_id: Uuid::now_v7(),
|
||||
is_primary: true,
|
||||
name: String::from("Adam"),
|
||||
},
|
||||
},
|
||||
QuoteLine {
|
||||
id: Uuid::now_v7(),
|
||||
content: String::from("Adam, ona nie ma kolan."),
|
||||
attribution: Name {
|
||||
id: Uuid::nil(),
|
||||
created_by: Uuid::max(),
|
||||
person_id: Uuid::now_v7(),
|
||||
is_primary: true,
|
||||
name: String::from("Mollin"),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use axum::{
|
||||
Form,
|
||||
extract::Request,
|
||||
extract::{Request, State},
|
||||
http::HeaderMap,
|
||||
response::{IntoResponse, Redirect, Response},
|
||||
};
|
||||
@@ -8,19 +8,23 @@ use maud::{PreEscaped, html};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
database::{self},
|
||||
MnemoState,
|
||||
error::CompositeError,
|
||||
logs::{LogAction, LogEntry},
|
||||
users::{
|
||||
User,
|
||||
auth::{AuthError, UserAuthRequired, UserAuthenticate},
|
||||
auth::{UserAuthRequired, UserAuthenticate},
|
||||
handle::UserHandle,
|
||||
},
|
||||
web::{components::nav::nav, icons, pages::base},
|
||||
};
|
||||
|
||||
pub async fn page(req: Request) -> Result<Response, AuthError> {
|
||||
let u = match User::authenticate(req.headers())? {
|
||||
pub async fn page(
|
||||
State(state): State<MnemoState>,
|
||||
req: Request,
|
||||
) -> Result<Response, CompositeError> {
|
||||
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()),
|
||||
};
|
||||
@@ -77,25 +81,26 @@ pub struct HandleForm {
|
||||
handle: UserHandle,
|
||||
}
|
||||
pub async fn change_handle(
|
||||
State(state): State<MnemoState>,
|
||||
headers: HeaderMap,
|
||||
Form(form): Form<HandleForm>,
|
||||
) -> Result<Response, CompositeError> {
|
||||
let mut u = User::authenticate(&headers)?.required()?;
|
||||
let mut conn = database::conn()?;
|
||||
let tx = conn.transaction()?;
|
||||
let mut tx = state.pool.begin().await?;
|
||||
let mut u = User::authenticate(&mut *tx, &headers).await?.required()?;
|
||||
|
||||
let oldhandle = u.handle.as_str().to_string();
|
||||
u.set_handle(&tx, form.handle)?;
|
||||
u.set_handle(&mut *tx, form.handle).await?;
|
||||
LogEntry::new(
|
||||
&tx,
|
||||
&mut *tx,
|
||||
u.clone(),
|
||||
LogAction::ChangeUserHandle {
|
||||
id: u.id,
|
||||
old: oldhandle,
|
||||
new: u.handle.as_str().to_string(),
|
||||
},
|
||||
)?;
|
||||
tx.commit()?;
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
Ok(Redirect::to("/user-settings").into_response())
|
||||
}
|
||||
|
||||
@@ -104,13 +109,14 @@ pub struct PasswordForm {
|
||||
password: String,
|
||||
}
|
||||
pub async fn change_password(
|
||||
State(state): State<MnemoState>,
|
||||
headers: HeaderMap,
|
||||
Form(form): Form<PasswordForm>,
|
||||
) -> Result<Response, CompositeError> {
|
||||
let mut u = User::authenticate(&headers)?.required()?;
|
||||
let mut conn = database::conn()?;
|
||||
let tx = conn.transaction()?;
|
||||
u.set_password(&tx, Some(&form.password))?;
|
||||
tx.commit()?;
|
||||
let mut tx = state.pool.begin().await?;
|
||||
let mut u = User::authenticate(&mut *tx, &headers).await?.required()?;
|
||||
|
||||
u.set_password(&mut *tx, Some(&form.password)).await?;
|
||||
tx.commit().await?;
|
||||
Ok(Redirect::to("/user-settings").into_response())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user