chore: 为 SECTL Auth 登录链路添加耗时日志

定位点击登录到浏览器弹出之间约 1 分钟延迟的来源,在前端
(handleOAuthLogin / getAuthorizationUrl) 与 Rust (oauth_start_callback_server)
各步骤加 +Nms 相对耗时日志。

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
JSR
2026-07-05 18:44:59 +08:00
parent e40ba986b2
commit b46acd7581
3 changed files with 46 additions and 4 deletions
+24 -3
View File
@@ -36,12 +36,24 @@ pub async fn oauth_start_callback_server(
app_handle: tauri::AppHandle, app_handle: tauri::AppHandle,
_state: State<'_, Arc<RwLock<AppState>>>, _state: State<'_, Arc<RwLock<AppState>>>,
) -> Result<IpcResponse<OAuthServerStartResult>, String> { ) -> Result<IpcResponse<OAuthServerStartResult>, String> {
let t0 = std::time::Instant::now();
let log = |step: &str| {
println!(
"[OAuth Callback] {} +{}ms",
step,
t0.elapsed().as_millis()
);
};
log("oauth_start_callback_server enter");
let mut shutdown_tx = OAUTH_SERVER_SHUTDOWN.lock().await; let mut shutdown_tx = OAUTH_SERVER_SHUTDOWN.lock().await;
log("after acquire shutdown lock");
// 如果服务器已经在运行,直接返回 URL // 如果服务器已经在运行,直接返回 URL
if shutdown_tx.is_some() { if shutdown_tx.is_some() {
let port = OAUTH_CALLBACK_PORT; let port = OAUTH_CALLBACK_PORT;
let url = format!("http://127.0.0.1:{}/oauth/callback", port); let url = format!("http://127.0.0.1:{}/oauth/callback", port);
log("server already running, return early");
return Ok(IpcResponse::success(OAuthServerStartResult { url, port })); return Ok(IpcResponse::success(OAuthServerStartResult { url, port }));
} }
@@ -51,19 +63,26 @@ pub async fn oauth_start_callback_server(
let url_for_spawn = url.clone(); let url_for_spawn = url.clone();
// 尝试绑定;若端口被占用,强杀占用进程后重试一次 // 尝试绑定;若端口被占用,强杀占用进程后重试一次
log("before TcpListener::bind");
let listener = match tokio::net::TcpListener::bind(addr).await { let listener = match tokio::net::TcpListener::bind(addr).await {
Ok(l) => l, Ok(l) => {
log("after TcpListener::bind (ok)");
l
}
Err(e) => { Err(e) => {
if e.kind() == std::io::ErrorKind::AddrInUse { if e.kind() == std::io::ErrorKind::AddrInUse {
println!( println!(
"[OAuth Callback] 端口 {} 被占用,尝试强杀占用进程", "[OAuth Callback] 端口 {} 被占用,尝试强杀占用进程 +{}ms",
port port,
t0.elapsed().as_millis()
); );
if let Err(kill_err) = kill_processes_on_port(port) { if let Err(kill_err) = kill_processes_on_port(port) {
eprintln!("[OAuth Callback] 强杀端口占用进程失败: {}", kill_err); eprintln!("[OAuth Callback] 强杀端口占用进程失败: {}", kill_err);
} }
log("after kill_processes_on_port");
// 给系统一点时间回收端口 // 给系统一点时间回收端口
tokio::time::sleep(std::time::Duration::from_millis(300)).await; tokio::time::sleep(std::time::Duration::from_millis(300)).await;
log("after sleep 300ms");
tokio::net::TcpListener::bind(addr) tokio::net::TcpListener::bind(addr)
.await .await
.map_err(|e| format!("绑定回调服务器端口 {} 失败: {}", port, e))? .map_err(|e| format!("绑定回调服务器端口 {} 失败: {}", port, e))?
@@ -76,6 +95,7 @@ pub async fn oauth_start_callback_server(
let (tx, rx) = oneshot::channel::<()>(); let (tx, rx) = oneshot::channel::<()>();
*shutdown_tx = Some(tx); *shutdown_tx = Some(tx);
drop(shutdown_tx); drop(shutdown_tx);
log("after store shutdown tx");
let app_handle_clone = app_handle.clone(); let app_handle_clone = app_handle.clone();
@@ -95,6 +115,7 @@ pub async fn oauth_start_callback_server(
} }
} }
}); });
log("after tokio::spawn, returning");
Ok(IpcResponse::success(OAuthServerStartResult { url, port })) Ok(IpcResponse::success(OAuthServerStartResult { url, port }))
} }
+12
View File
@@ -127,12 +127,18 @@ export function OAuthLogin({ visible, onClose, onSuccess }: OAuthLoginProps) {
} }
setLoading(true) setLoading(true)
const t0 = performance.now()
const log = (step: string) =>
console.log(`[OAuthLogin] ${step} +${Math.round(performance.now() - t0)}ms`)
log("handleOAuthLogin start")
try { try {
// 启动本地 HTTP 回调服务器 (端口 51267,被占用则强杀已有进程) // 启动本地 HTTP 回调服务器 (端口 51267,被占用则强杀已有进程)
const api = (window as any).api const api = (window as any).api
if (api && typeof api.oauthStartCallbackServer === "function") { if (api && typeof api.oauthStartCallbackServer === "function") {
log("before oauthStartCallbackServer")
const res = await api.oauthStartCallbackServer() const res = await api.oauthStartCallbackServer()
log(`after oauthStartCallbackServer success=${res?.success}`)
if (!res?.success) { if (!res?.success) {
throw new Error(res?.message || "启动回调服务器失败") throw new Error(res?.message || "启动回调服务器失败")
} }
@@ -142,19 +148,25 @@ export function OAuthLogin({ visible, onClose, onSuccess }: OAuthLoginProps) {
} }
} }
log("before getAuthorizationUrl")
const authUrl = await sectlAuth.getAuthorizationUrl() const authUrl = await sectlAuth.getAuthorizationUrl()
log(`after getAuthorizationUrl url=${authUrl.slice(0, 60)}...`)
// 使用 Tauri shell 打开系统浏览器 // 使用 Tauri shell 打开系统浏览器
log("before open(authUrl)")
await open(authUrl) await open(authUrl)
log("after open(authUrl)")
// 设置超时 // 设置超时
setTimeout(() => { setTimeout(() => {
if (loading && !completedRef.current) { if (loading && !completedRef.current) {
log("login timeout, stopping callback server")
stopCallbackServer() stopCallbackServer()
setLoading(false) setLoading(false)
message.warning("登录超时,请重试") message.warning("登录超时,请重试")
} }
}, 300000) }, 300000)
} catch (error: any) { } catch (error: any) {
log(`handleOAuthLogin error: ${error?.message}`)
message.error(error.message || "登录失败") message.error(error.message || "登录失败")
await stopCallbackServer() await stopCallbackServer()
setLoading(false) setLoading(false)
+10 -1
View File
@@ -120,9 +120,16 @@ class SectlAuthService {
} }
async getAuthorizationUrl(scope?: string[]): Promise<string> { async getAuthorizationUrl(scope?: string[]): Promise<string> {
const t0 = performance.now()
const log = (step: string) =>
console.log(`[sectlAuth.getAuthorizationUrl] ${step} +${Math.round(performance.now() - t0)}ms`)
const state = this.generateRandomState() const state = this.generateRandomState()
log("after generateRandomState")
this.codeVerifier = await generateCodeVerifier() this.codeVerifier = await generateCodeVerifier()
log("after generateCodeVerifier")
const codeChallenge = await generateCodeChallenge(this.codeVerifier) const codeChallenge = await generateCodeChallenge(this.codeVerifier)
log("after generateCodeChallenge")
// 保存 code_verifier 到 localStorage,以便 deep link 回调时使用 // 保存 code_verifier 到 localStorage,以便 deep link 回调时使用
localStorage.setItem("sectl_code_verifier", this.codeVerifier) localStorage.setItem("sectl_code_verifier", this.codeVerifier)
@@ -140,7 +147,9 @@ class SectlAuthService {
params.set("scope", scope.join(" ")) params.set("scope", scope.join(" "))
} }
return `${SECTL_CONFIG.authUrl}/oauth/authorize?${params.toString()}` const url = `${SECTL_CONFIG.authUrl}/oauth/authorize?${params.toString()}`
log("done")
return url
} }
async authorize(scope?: string[]): Promise<TokenData> { async authorize(scope?: string[]): Promise<TokenData> {