53 lines
1.3 KiB
Rust
53 lines
1.3 KiB
Rust
use axum::{extract::State, http::StatusCode, Json};
|
|
use chrono::Duration;
|
|
use serde::Deserialize;
|
|
use serde_json::{json, Value};
|
|
use std::sync::Arc;
|
|
|
|
use crate::{
|
|
auth::{verify, Session},
|
|
error::AppError,
|
|
state::AppState,
|
|
};
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct LoginRequest {
|
|
pub username: String,
|
|
pub password: String,
|
|
}
|
|
|
|
pub async fn login(
|
|
State(state): State<Arc<AppState>>,
|
|
Json(payload): Json<LoginRequest>,
|
|
) -> Result<Json<Value>, AppError> {
|
|
let user = sqlx::query!("SELECT * FROM users WHERE name = $1", payload.username)
|
|
.fetch_optional(&state.database)
|
|
.await?;
|
|
|
|
let invalid = || -> AppError {
|
|
AppError::ClientError {
|
|
status: StatusCode::UNAUTHORIZED,
|
|
code: "invalid-login".into(),
|
|
message: "No matching user was found".into(),
|
|
}
|
|
};
|
|
|
|
let user = user.ok_or_else(invalid)?;
|
|
let plaintext = user.password.ok_or_else(invalid)?;
|
|
|
|
if !verify(&payload.password, &plaintext)? {
|
|
return Err(invalid());
|
|
}
|
|
|
|
let session = Session::create(
|
|
&state.database,
|
|
user.id,
|
|
Duration::seconds(state.config.session_duration.into()),
|
|
)
|
|
.await?;
|
|
|
|
Ok(Json(
|
|
json!({ "session_token": session.token(), "expires_at": session.expires_at }),
|
|
))
|
|
}
|