Signing up to an account that exists signs in; reset a password on the server
- Ticking 建立新帳號 for an account that already exists signs in with it; when that fails too, the message says the server has an account and how to reset a forgotten password. - anybot-relay --data DIR --reset-password NAME (only while the server is stopped; asks twice, not shown; signs out that account's phones). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
This commit is contained in:
parent
365dc87e54
commit
dd27ed7c4a
|
|
@ -866,6 +866,10 @@ fn main() {
|
|||
let port: u16 = arg("--port").and_then(|p| p.parse().ok()).or_else(|| std::env::var("PORT").ok().and_then(|p| p.parse().ok())).unwrap_or(8080);
|
||||
let bind = arg("--bind").unwrap_or_else(|| "0.0.0.0".into());
|
||||
let dir = PathBuf::from(arg("--data").unwrap_or_else(|| "relay-data".into()));
|
||||
// Forgot the password: only someone on this computer can set a new one.
|
||||
if let Some(user) = arg("--reset-password") {
|
||||
std::process::exit(reset_password(&dir, &user.trim().to_lowercase(), port));
|
||||
}
|
||||
let relay = Arc::new(Relay::load(dir.clone()));
|
||||
let listener = std::net::TcpListener::bind((bind.as_str(), port)).unwrap_or_else(|e| {
|
||||
eprintln!("無法使用連接埠 {port}:{e}");
|
||||
|
|
@ -884,6 +888,52 @@ fn main() {
|
|||
httpd::serve_upgrade(listener, Arc::new(move |req| r.handle(req)), Some(Arc::new(move |req, c| w.ws(req, c))), Arc::new(|| true));
|
||||
}
|
||||
|
||||
/// `anybot-relay --data DIR --reset-password NAME`: a new password for an account (asked twice,
|
||||
/// not shown). The server must be stopped first, or it would write its own copy back.
|
||||
fn reset_password(dir: &std::path::Path, user: &str, port: u16) -> i32 {
|
||||
if std::net::TcpStream::connect_timeout(&std::net::SocketAddr::from(([127, 0, 0, 1], port)), Duration::from_millis(500)).is_ok() {
|
||||
eprintln!("帳號伺服器還在執行(埠 {port})。先停掉它再重設密碼,之後再啟動。");
|
||||
return 1;
|
||||
}
|
||||
let relay = Relay::load(dir.to_path_buf());
|
||||
if !relay.data.lock().unwrap().users.iter().any(|u| u.name == user) {
|
||||
eprintln!("{} 裡沒有帳號「{user}」。", dir.display());
|
||||
return 1;
|
||||
}
|
||||
let ask = |prompt: &str| -> String {
|
||||
use std::io::Write;
|
||||
eprint!("{prompt}");
|
||||
let _ = std::io::stderr().flush();
|
||||
let hide = std::process::Command::new("stty").arg("-echo").stdin(std::process::Stdio::inherit()).stderr(std::process::Stdio::null()).status().map(|s| s.success()).unwrap_or(false);
|
||||
let mut s = String::new();
|
||||
let _ = std::io::stdin().read_line(&mut s);
|
||||
if hide {
|
||||
let _ = std::process::Command::new("stty").arg("echo").stdin(std::process::Stdio::inherit()).stderr(std::process::Stdio::null()).status();
|
||||
}
|
||||
eprintln!();
|
||||
s.trim_end_matches(['\n', '\r']).to_string()
|
||||
};
|
||||
let pw = ask(&format!("「{user}」的新密碼(至少 8 個字):"));
|
||||
if pw.chars().count() < 8 {
|
||||
eprintln!("密碼至少 8 個字,沒有改。");
|
||||
return 1;
|
||||
}
|
||||
if ask("再輸入一次:") != pw {
|
||||
eprintln!("兩次不一樣,沒有改。");
|
||||
return 1;
|
||||
}
|
||||
let mut d = relay.data.lock().unwrap();
|
||||
if let Some(u) = d.users.iter_mut().find(|u| u.name == user) {
|
||||
u.pass = httpd::hash_password(&pw);
|
||||
}
|
||||
// Phones signed in with the old password sign in again.
|
||||
d.sessions.retain(|s| s.user != user);
|
||||
relay.save(&d);
|
||||
eprintln!("已改好「{user}」的密碼。啟動帳號伺服器後,用新密碼登入。");
|
||||
eprintln!("(帳號備份是用舊密碼加密的,之後要先在電腦上按一次「立刻備份」。)");
|
||||
0
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -756,7 +756,16 @@ pub fn relay_join(base_settings: &Settings, url: &str, user: &str, password: &st
|
|||
return Err("帳號伺服器要用 https:// 開頭的網址(這台電腦或 Tailscale 位址可以用 http://)".into());
|
||||
}
|
||||
if create {
|
||||
relay_post(&format!("{url}/relay/signup"), "", &json!({"user": user, "password": password, "invite": invite}))?;
|
||||
if let Err(e) = relay_post(&format!("{url}/relay/signup"), "", &json!({"user": user, "password": password, "invite": invite})) {
|
||||
// The account may be there already (made earlier, or on the phone): just sign in with it.
|
||||
if relay_post(&format!("{url}/relay/login"), "", &json!({"user": user, "password": password, "device": host_name})).is_err() {
|
||||
return Err(if e.contains("不開放註冊") || e.contains("邀請碼") {
|
||||
"這台帳號伺服器已經有帳號了,不能再註冊新的。請取消勾選「建立新帳號」,用已經有的帳號登入。忘記密碼的話,在帳號伺服器那台電腦執行:anybot-relay --data 資料夾 --reset-password 帳號".into()
|
||||
} else {
|
||||
e
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
let v = relay_post(&format!("{url}/relay/login"), "", &json!({"user": user, "password": password, "device": host_name}))?;
|
||||
let session = v["token"].as_str().ok_or("帳號伺服器沒有給登入憑證")?.to_string();
|
||||
|
|
|
|||
Loading…
Reference in New Issue