Record login for users
This commit is contained in:
@@ -15,6 +15,8 @@ use axum::{
|
||||
};
|
||||
use axum::{http::StatusCode, response::Redirect};
|
||||
use secrecy::SecretString;
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct LoginFormData {
|
||||
@@ -50,6 +52,9 @@ pub async fn post_login(
|
||||
tracing::Span::current().record("username", tracing::field::display(&credentials.username));
|
||||
let (user_id, role) = validate_credentials(credentials, &connection_pool).await?;
|
||||
tracing::Span::current().record("user_id", tracing::field::display(&user_id));
|
||||
record_login(&connection_pool, &user_id)
|
||||
.await
|
||||
.context("Failed to register new login event.")?;
|
||||
|
||||
session.renew().await.context("Failed to renew session.")?;
|
||||
session
|
||||
@@ -69,3 +74,11 @@ pub async fn post_login(
|
||||
headers.insert("HX-Redirect", "/dashboard".parse().unwrap());
|
||||
Ok((StatusCode::OK, headers).into_response())
|
||||
}
|
||||
|
||||
#[tracing::instrument(name = "Recording new login event", skip_all, fields(user_id = %user_id))]
|
||||
async fn record_login(connection_pool: &PgPool, user_id: &Uuid) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!("INSERT INTO user_logins (user_id) VALUES ($1)", user_id)
|
||||
.execute(connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ use axum::{
|
||||
extract::State,
|
||||
response::{Html, IntoResponse, Redirect, Response},
|
||||
};
|
||||
use chrono::Utc;
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
use validator::Validate;
|
||||
@@ -77,7 +78,8 @@ async fn get_posts(
|
||||
sqlx::query_as!(
|
||||
PostEntry,
|
||||
r#"
|
||||
SELECT p.post_id, p.author_id, u.username AS author, p.title, p.content, p.published_at
|
||||
SELECT p.post_id, p.author_id, u.username AS author,
|
||||
p.title, p.content, p.published_at, p.last_modified
|
||||
FROM posts p
|
||||
LEFT JOIN users u ON p.author_id = u.user_id
|
||||
ORDER BY p.published_at DESC
|
||||
@@ -99,7 +101,8 @@ pub async fn get_posts_page(
|
||||
sqlx::query_as!(
|
||||
PostEntry,
|
||||
r#"
|
||||
SELECT p.post_id, p.author_id, u.username AS author, p.title, p.content, p.published_at
|
||||
SELECT p.post_id, p.author_id, u.username AS author,
|
||||
p.title, p.content, p.published_at, p.last_modified
|
||||
FROM posts p
|
||||
LEFT JOIN users u ON p.author_id = u.user_id
|
||||
ORDER BY p.published_at DESC
|
||||
@@ -151,10 +154,11 @@ pub async fn update_post(
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE posts
|
||||
SET title = $1, content = $2 WHERE post_id = $3
|
||||
SET title = $1, content = $2, last_modified = $3 WHERE post_id = $4
|
||||
",
|
||||
form.title,
|
||||
form.content,
|
||||
Utc::now(),
|
||||
post_id
|
||||
)
|
||||
.execute(&connection_pool)
|
||||
@@ -257,7 +261,8 @@ async fn get_post_data(
|
||||
sqlx::query_as!(
|
||||
PostEntry,
|
||||
r#"
|
||||
SELECT p.post_id, p.author_id, u.username AS author, p.title, p.content, p.published_at
|
||||
SELECT p.post_id, p.author_id, u.username AS author,
|
||||
p.title, p.content, p.published_at, last_modified
|
||||
FROM posts p
|
||||
LEFT JOIN users u ON p.author_id = u.user_id
|
||||
WHERE p.post_id = $1
|
||||
|
||||
@@ -91,7 +91,11 @@ pub async fn update_user(
|
||||
return Ok(template.into_response());
|
||||
}
|
||||
let updated_full_name = form.full_name.trim();
|
||||
let bio = form.bio.trim();
|
||||
let bio = {
|
||||
let bio = form.bio.trim();
|
||||
if bio.is_empty() { None } else { Some(bio) }
|
||||
};
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE users
|
||||
@@ -255,13 +259,32 @@ pub async fn user_profile(
|
||||
let posts = fetch_user_posts(&connection_pool, &user.user_id)
|
||||
.await
|
||||
.context("Could not fetch user posts.")?;
|
||||
let session_username = session
|
||||
.get_username()
|
||||
let session_user_id = session
|
||||
.get_user_id()
|
||||
.await
|
||||
.context("Could not fetch session username.")?;
|
||||
let profile_user_id =
|
||||
sqlx::query!("SELECT user_id FROM users WHERE username = $1", username)
|
||||
.fetch_one(&connection_pool)
|
||||
.await
|
||||
.context("Could not fetch profile user id.")?
|
||||
.user_id;
|
||||
let last_seen = sqlx::query!(
|
||||
"
|
||||
SELECT login_time FROM user_logins
|
||||
WHERE user_id = $1
|
||||
ORDER BY login_time DESC
|
||||
",
|
||||
profile_user_id
|
||||
)
|
||||
.fetch_optional(&connection_pool)
|
||||
.await
|
||||
.context("Failed to fetch last user login")?
|
||||
.map(|r| r.login_time);
|
||||
let template = HtmlTemplate(UserTemplate {
|
||||
user,
|
||||
session_username,
|
||||
session_user_id,
|
||||
last_seen,
|
||||
posts,
|
||||
});
|
||||
Ok(template.into_response())
|
||||
@@ -299,7 +322,8 @@ async fn fetch_user_posts(
|
||||
sqlx::query_as!(
|
||||
PostEntry,
|
||||
r#"
|
||||
SELECT p.author_id, u.username as author, p.post_id, p.title, p.content, p.published_at
|
||||
SELECT p.author_id, u.username as author,
|
||||
p.post_id, p.title, p.content, p.published_at, p.last_modified
|
||||
FROM posts p
|
||||
INNER JOIN users u ON p.author_id = u.user_id
|
||||
WHERE p.author_id = $1
|
||||
|
||||
Reference in New Issue
Block a user