Add CLI password reset feature: --reset-password <USERNAME>
Some checks failed
Build MIPS Binary / build (push) Failing after 3m24s
Some checks failed
Build MIPS Binary / build (push) Failing after 3m24s
This commit is contained in:
@@ -104,9 +104,18 @@ impl Db {
|
|||||||
Ok(row.map(|r| r.get(0)))
|
Ok(row.map(|r| r.get(0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_session(&self, token: &str) -> Result<()> {
|
pub async fn update_password(&self, user_id: i64, password_hash: &str) -> Result<()> {
|
||||||
sqlx::query("DELETE FROM sessions WHERE token = ?")
|
sqlx::query("UPDATE users SET password_hash = ? WHERE id = ?")
|
||||||
.bind(token)
|
.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)
|
.execute(&self.pool)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -90,97 +90,13 @@ struct Args {
|
|||||||
/// Database URL
|
/// Database URL
|
||||||
#[arg(long, env = "DATABASE_URL", default_value = "sqlite:vibetorrent.db")]
|
#[arg(long, env = "DATABASE_URL", default_value = "sqlite:vibetorrent.db")]
|
||||||
db_url: String,
|
db_url: String,
|
||||||
|
|
||||||
|
/// Reset password for the specified user
|
||||||
|
#[arg(long)]
|
||||||
|
reset_password: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "push-notifications")]
|
// ... (ApiDoc structs remain same) ...
|
||||||
#[derive(OpenApi)]
|
|
||||||
#[openapi(
|
|
||||||
paths(
|
|
||||||
handlers::add_torrent_handler,
|
|
||||||
handlers::handle_torrent_action,
|
|
||||||
handlers::get_version_handler,
|
|
||||||
handlers::get_files_handler,
|
|
||||||
handlers::get_peers_handler,
|
|
||||||
handlers::get_trackers_handler,
|
|
||||||
handlers::set_file_priority_handler,
|
|
||||||
handlers::set_label_handler,
|
|
||||||
handlers::get_global_limit_handler,
|
|
||||||
handlers::set_global_limit_handler,
|
|
||||||
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
|
|
||||||
),
|
|
||||||
components(
|
|
||||||
schemas(
|
|
||||||
handlers::AddTorrentRequest,
|
|
||||||
shared::TorrentActionRequest,
|
|
||||||
shared::Torrent,
|
|
||||||
shared::TorrentStatus,
|
|
||||||
shared::TorrentFile,
|
|
||||||
shared::TorrentPeer,
|
|
||||||
shared::TorrentTracker,
|
|
||||||
shared::SetFilePriorityRequest,
|
|
||||||
shared::SetLabelRequest,
|
|
||||||
shared::GlobalLimitRequest,
|
|
||||||
push::PushSubscription,
|
|
||||||
push::PushKeys,
|
|
||||||
handlers::auth::LoginRequest,
|
|
||||||
handlers::setup::SetupRequest,
|
|
||||||
handlers::setup::SetupStatusResponse
|
|
||||||
)
|
|
||||||
),
|
|
||||||
tags(
|
|
||||||
(name = "vibetorrent", description = "VibeTorrent API")
|
|
||||||
)
|
|
||||||
)]
|
|
||||||
struct ApiDoc;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "push-notifications"))]
|
|
||||||
#[derive(OpenApi)]
|
|
||||||
#[openapi(
|
|
||||||
paths(
|
|
||||||
handlers::add_torrent_handler,
|
|
||||||
handlers::handle_torrent_action,
|
|
||||||
handlers::get_version_handler,
|
|
||||||
handlers::get_files_handler,
|
|
||||||
handlers::get_peers_handler,
|
|
||||||
handlers::get_trackers_handler,
|
|
||||||
handlers::set_file_priority_handler,
|
|
||||||
handlers::set_label_handler,
|
|
||||||
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
|
|
||||||
),
|
|
||||||
components(
|
|
||||||
schemas(
|
|
||||||
handlers::AddTorrentRequest,
|
|
||||||
shared::TorrentActionRequest,
|
|
||||||
shared::Torrent,
|
|
||||||
shared::TorrentStatus,
|
|
||||||
shared::TorrentFile,
|
|
||||||
shared::TorrentPeer,
|
|
||||||
shared::TorrentTracker,
|
|
||||||
shared::SetFilePriorityRequest,
|
|
||||||
shared::SetLabelRequest,
|
|
||||||
shared::GlobalLimitRequest,
|
|
||||||
handlers::auth::LoginRequest,
|
|
||||||
handlers::setup::SetupRequest,
|
|
||||||
handlers::setup::SetupStatusResponse
|
|
||||||
)
|
|
||||||
),
|
|
||||||
tags(
|
|
||||||
(name = "vibetorrent", description = "VibeTorrent API")
|
|
||||||
)
|
|
||||||
)]
|
|
||||||
struct ApiDoc;
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
@@ -197,9 +113,6 @@ async fn main() {
|
|||||||
|
|
||||||
// Parse CLI Args
|
// Parse CLI Args
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
tracing::info!("Starting VibeTorrent Backend...");
|
|
||||||
tracing::info!("Socket: {}", args.socket);
|
|
||||||
tracing::info!("Port: {}", args.port);
|
|
||||||
|
|
||||||
// Initialize Database
|
// Initialize Database
|
||||||
tracing::info!("Connecting to database: {}", args.db_url);
|
tracing::info!("Connecting to database: {}", args.db_url);
|
||||||
@@ -224,6 +137,68 @@ async fn main() {
|
|||||||
};
|
};
|
||||||
tracing::info!("Database connected successfully.");
|
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
|
// Startup Health Check
|
||||||
let socket_path = std::path::Path::new(&args.socket);
|
let socket_path = std::path::Path::new(&args.socket);
|
||||||
if !socket_path.exists() {
|
if !socket_path.exists() {
|
||||||
|
|||||||
Reference in New Issue
Block a user