Compare commits
6 Commits
release-20
...
release-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
643b83ac21 | ||
|
|
90b65240b2 | ||
|
|
69243a5590 | ||
|
|
10262142fc | ||
|
|
858a1c9b63 | ||
|
|
edfb7458f8 |
@@ -111,4 +111,21 @@ impl Db {
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update_password(&self, user_id: i64, password_hash: &str) -> Result<()> {
|
||||
sqlx::query("UPDATE users SET password_hash = ? WHERE id = ?")
|
||||
.bind(password_hash)
|
||||
.bind(user_id)
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_all_sessions_for_user(&self, user_id: i64) -> Result<()> {
|
||||
sqlx::query("DELETE FROM sessions WHERE user_id = ?")
|
||||
.bind(user_id)
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,10 @@ struct Args {
|
||||
/// Database URL
|
||||
#[arg(long, env = "DATABASE_URL", default_value = "sqlite:vibetorrent.db")]
|
||||
db_url: String,
|
||||
|
||||
/// Reset password for the specified user
|
||||
#[arg(long)]
|
||||
reset_password: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "push-notifications")]
|
||||
@@ -130,7 +134,8 @@ struct Args {
|
||||
push::PushKeys,
|
||||
handlers::auth::LoginRequest,
|
||||
handlers::setup::SetupRequest,
|
||||
handlers::setup::SetupStatusResponse
|
||||
handlers::setup::SetupStatusResponse,
|
||||
handlers::auth::UserResponse
|
||||
)
|
||||
),
|
||||
tags(
|
||||
@@ -173,7 +178,8 @@ struct ApiDoc;
|
||||
shared::GlobalLimitRequest,
|
||||
handlers::auth::LoginRequest,
|
||||
handlers::setup::SetupRequest,
|
||||
handlers::setup::SetupStatusResponse
|
||||
handlers::setup::SetupStatusResponse,
|
||||
handlers::auth::UserResponse
|
||||
)
|
||||
),
|
||||
tags(
|
||||
@@ -197,9 +203,6 @@ async fn main() {
|
||||
|
||||
// Parse CLI Args
|
||||
let args = Args::parse();
|
||||
tracing::info!("Starting VibeTorrent Backend...");
|
||||
tracing::info!("Socket: {}", args.socket);
|
||||
tracing::info!("Port: {}", args.port);
|
||||
|
||||
// Initialize Database
|
||||
tracing::info!("Connecting to database: {}", args.db_url);
|
||||
@@ -224,6 +227,68 @@ async fn main() {
|
||||
};
|
||||
tracing::info!("Database connected successfully.");
|
||||
|
||||
// Handle Password Reset
|
||||
if let Some(username) = args.reset_password {
|
||||
tracing::info!("Resetting password for user: {}", username);
|
||||
|
||||
// Check if user exists
|
||||
let user_result = db.get_user_by_username(&username).await;
|
||||
|
||||
match user_result {
|
||||
Ok(Some((user_id, _))) => {
|
||||
// Generate random password
|
||||
use rand::{distributions::Alphanumeric, Rng};
|
||||
let new_password: String = rand::thread_rng()
|
||||
.sample_iter(&Alphanumeric)
|
||||
.take(12)
|
||||
.map(char::from)
|
||||
.collect();
|
||||
|
||||
// Hash password (low cost for performance)
|
||||
let password_hash = match bcrypt::hash(&new_password, 6) {
|
||||
Ok(h) => h,
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to hash password: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
// Update in DB (using a direct query since db.rs doesn't have update_password yet)
|
||||
// We should add `update_password` to db.rs for cleaner code, but for now direct query is fine or we can extend Db.
|
||||
// Let's extend Db.rs first to be clean.
|
||||
if let Err(e) = db.update_password(user_id, &password_hash).await {
|
||||
tracing::error!("Failed to update password in DB: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
println!("--------------------------------------------------");
|
||||
println!("Password reset successfully for user: {}", username);
|
||||
println!("New Password: {}", new_password);
|
||||
println!("--------------------------------------------------");
|
||||
|
||||
// Invalidate existing sessions for security
|
||||
if let Err(e) = db.delete_all_sessions_for_user(user_id).await {
|
||||
tracing::warn!("Failed to invalidate existing sessions: {}", e);
|
||||
}
|
||||
|
||||
std::process::exit(0);
|
||||
},
|
||||
Ok(None) => {
|
||||
tracing::error!("User '{}' not found.", username);
|
||||
std::process::exit(1);
|
||||
},
|
||||
Err(e) => {
|
||||
tracing::error!("Database error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tracing::info!("Starting VibeTorrent Backend...");
|
||||
tracing::info!("Socket: {}", args.socket);
|
||||
tracing::info!("Port: {}", args.port);
|
||||
|
||||
// ... rest of the main function ...
|
||||
// Startup Health Check
|
||||
let socket_path = std::path::Path::new(&args.socket);
|
||||
if !socket_path.exists() {
|
||||
|
||||
@@ -69,6 +69,14 @@ pub fn App() -> impl IntoView {
|
||||
}
|
||||
|
||||
set_is_authenticated.set(true);
|
||||
|
||||
// If user is already authenticated but on login/setup page, redirect to home
|
||||
let pathname = window().location().pathname().unwrap_or_default();
|
||||
if pathname == "/login" || pathname == "/setup" {
|
||||
logging::log!("Already authenticated, redirecting to home");
|
||||
let navigate = use_navigate();
|
||||
navigate("/", Default::default());
|
||||
}
|
||||
} else {
|
||||
logging::log!("Not authenticated, redirecting to /login");
|
||||
let navigate = use_navigate();
|
||||
@@ -80,7 +88,6 @@ pub fn App() -> impl IntoView {
|
||||
}
|
||||
Err(e) => logging::error!("Network error checking auth status: {}", e),
|
||||
}
|
||||
|
||||
set_is_loading.set(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use leptos::*;
|
||||
use leptos_router::*;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use leptos::*;
|
||||
use leptos_router::*;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
||||
Reference in New Issue
Block a user