Fix compilation errors: Add missing dependencies, fix module visibility, and update Axum middleware types
Some checks failed
Build MIPS Binary / build (push) Failing after 3m27s
Some checks failed
Build MIPS Binary / build (push) Failing after 3m27s
This commit is contained in:
5
Cargo.lock
generated
5
Cargo.lock
generated
@@ -99,9 +99,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.100"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
@@ -341,6 +341,7 @@ dependencies = [
|
||||
name = "backend"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.8.8",
|
||||
"axum-extra",
|
||||
"base64 0.22.1",
|
||||
|
||||
@@ -37,3 +37,4 @@ sqlx = { version = "0.8", features = ["runtime-tokio", "sqlite"] }
|
||||
bcrypt = "0.17.0"
|
||||
axum-extra = { version = "0.9", features = ["cookie"] }
|
||||
rand = "0.8"
|
||||
anyhow = "1.0.101"
|
||||
|
||||
108
backend/src/handlers/auth.rs
Normal file
108
backend/src/handlers/auth.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
use crate::{db::Db, AppState};
|
||||
use axum::{
|
||||
extract::{State, Json},
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
use axum_extra::extract::cookie::{Cookie, CookieJar, SameSite};
|
||||
use time::Duration;
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub struct LoginRequest {
|
||||
username: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct UserResponse {
|
||||
username: String,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/auth/login",
|
||||
request_body = LoginRequest,
|
||||
responses(
|
||||
(status = 200, description = "Login successful"),
|
||||
(status = 401, description = "Invalid credentials"),
|
||||
(status = 500, description = "Internal server error")
|
||||
)
|
||||
)]
|
||||
pub async fn login_handler(
|
||||
State(state): State<AppState>,
|
||||
jar: CookieJar,
|
||||
Json(payload): Json<LoginRequest>,
|
||||
) -> impl IntoResponse {
|
||||
let user = match state.db.get_user_by_username(&payload.username).await {
|
||||
Ok(Some(u)) => u,
|
||||
Ok(None) => return (StatusCode::UNAUTHORIZED, "Invalid credentials").into_response(),
|
||||
Err(e) => {
|
||||
tracing::error!("DB error during login: {}", e);
|
||||
return (StatusCode::INTERNAL_SERVER_ERROR, "Database error").into_response();
|
||||
}
|
||||
};
|
||||
|
||||
let (user_id, password_hash) = user;
|
||||
|
||||
match bcrypt::verify(&payload.password, &password_hash) {
|
||||
Ok(true) => {
|
||||
// Create session
|
||||
let token: String = (0..32).map(|_| {
|
||||
use rand::{distributions::Alphanumeric, Rng};
|
||||
rand::thread_rng().sample(Alphanumeric) as char
|
||||
}).collect();
|
||||
|
||||
// Expires in 30 days
|
||||
let expires_in = 60 * 60 * 24 * 30;
|
||||
let expires_at = time::OffsetDateTime::now_utc().unix_timestamp() + expires_in;
|
||||
|
||||
if let Err(e) = state.db.create_session(user_id, &token, expires_at).await {
|
||||
tracing::error!("Failed to create session: {}", e);
|
||||
return (StatusCode::INTERNAL_SERVER_ERROR, "Failed to create session").into_response();
|
||||
}
|
||||
|
||||
let cookie = Cookie::build(("auth_token", token))
|
||||
.path("/")
|
||||
.http_only(true)
|
||||
.same_site(SameSite::Strict)
|
||||
.max_age(Duration::seconds(expires_in))
|
||||
.build();
|
||||
|
||||
(StatusCode::OK, jar.add(cookie), "Login successful").into_response()
|
||||
}
|
||||
_ => (StatusCode::UNAUTHORIZED, "Invalid credentials").into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn logout_handler(
|
||||
State(state): State<AppState>,
|
||||
jar: CookieJar,
|
||||
) -> impl IntoResponse {
|
||||
if let Some(token) = jar.get("auth_token") {
|
||||
let _ = state.db.delete_session(token.value()).await;
|
||||
}
|
||||
|
||||
let cookie = Cookie::build(("auth_token", ""))
|
||||
.path("/")
|
||||
.http_only(true)
|
||||
.max_age(Duration::seconds(-1)) // Expire immediately
|
||||
.build();
|
||||
|
||||
(StatusCode::OK, jar.add(cookie), "Logged out").into_response()
|
||||
}
|
||||
|
||||
pub async fn check_auth_handler(
|
||||
State(state): State<AppState>,
|
||||
jar: CookieJar,
|
||||
) -> impl IntoResponse {
|
||||
if let Some(token) = jar.get("auth_token") {
|
||||
match state.db.get_session_user(token.value()).await {
|
||||
Ok(Some(_)) => return StatusCode::OK.into_response(),
|
||||
_ => {} // Invalid session
|
||||
}
|
||||
}
|
||||
|
||||
StatusCode::UNAUTHORIZED.into_response()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{db::Db, AppState};
|
||||
use crate::AppState;
|
||||
use axum::{
|
||||
extract::{State, Json},
|
||||
http::StatusCode,
|
||||
@@ -18,6 +18,13 @@ pub struct SetupStatusResponse {
|
||||
completed: bool,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/setup/status",
|
||||
responses(
|
||||
(status = 200, description = "Setup status", body = SetupStatusResponse)
|
||||
)
|
||||
)]
|
||||
pub async fn get_setup_status_handler(State(state): State<AppState>) -> impl IntoResponse {
|
||||
let completed = match state.db.has_users().await {
|
||||
Ok(has) => has,
|
||||
@@ -29,6 +36,17 @@ pub async fn get_setup_status_handler(State(state): State<AppState>) -> impl Int
|
||||
Json(SetupStatusResponse { completed }).into_response()
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/setup",
|
||||
request_body = SetupRequest,
|
||||
responses(
|
||||
(status = 200, description = "Setup completed"),
|
||||
(status = 400, description = "Invalid request"),
|
||||
(status = 403, description = "Setup already completed"),
|
||||
(status = 500, description = "Internal server error")
|
||||
)
|
||||
)]
|
||||
pub async fn setup_handler(
|
||||
State(state): State<AppState>,
|
||||
Json(payload): Json<SetupRequest>,
|
||||
|
||||
@@ -15,6 +15,7 @@ use axum::{
|
||||
extract::Request,
|
||||
response::Response,
|
||||
http::StatusCode,
|
||||
body::Body,
|
||||
};
|
||||
use axum_extra::extract::cookie::CookieJar;
|
||||
use clap::Parser;
|
||||
@@ -46,7 +47,7 @@ pub struct AppState {
|
||||
async fn auth_middleware(
|
||||
state: axum::extract::State<AppState>,
|
||||
jar: CookieJar,
|
||||
request: Request,
|
||||
request: Request<Body>,
|
||||
next: Next,
|
||||
) -> Result<Response, StatusCode> {
|
||||
// Skip auth for public paths
|
||||
@@ -110,6 +111,8 @@ struct Args {
|
||||
handlers::get_push_public_key_handler,
|
||||
handlers::subscribe_push_handler,
|
||||
handlers::auth::login_handler,
|
||||
handlers::auth::logout_handler,
|
||||
handlers::auth::check_auth_handler,
|
||||
handlers::setup::setup_handler,
|
||||
handlers::setup::get_setup_status_handler
|
||||
),
|
||||
@@ -128,7 +131,8 @@ struct Args {
|
||||
push::PushSubscription,
|
||||
push::PushKeys,
|
||||
handlers::auth::LoginRequest,
|
||||
handlers::setup::SetupRequest
|
||||
handlers::setup::SetupRequest,
|
||||
handlers::setup::SetupStatusResponse
|
||||
)
|
||||
),
|
||||
tags(
|
||||
@@ -152,6 +156,8 @@ struct ApiDoc;
|
||||
handlers::get_global_limit_handler,
|
||||
handlers::set_global_limit_handler,
|
||||
handlers::auth::login_handler,
|
||||
handlers::auth::logout_handler,
|
||||
handlers::auth::check_auth_handler,
|
||||
handlers::setup::setup_handler,
|
||||
handlers::setup::get_setup_status_handler
|
||||
),
|
||||
@@ -168,7 +174,8 @@ struct ApiDoc;
|
||||
shared::SetLabelRequest,
|
||||
shared::GlobalLimitRequest,
|
||||
handlers::auth::LoginRequest,
|
||||
handlers::setup::SetupRequest
|
||||
handlers::setup::SetupRequest,
|
||||
handlers::setup::SetupStatusResponse
|
||||
)
|
||||
),
|
||||
tags(
|
||||
@@ -210,7 +217,7 @@ async fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
let db = match db::Db::new(&args.db_url).await {
|
||||
let db: db::Db = match db::Db::new(&args.db_url).await {
|
||||
Ok(db) => db,
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to connect to database: {}", e);
|
||||
|
||||
Reference in New Issue
Block a user