fix(audit): P1 心跳自启动 + refreshToken body + 类型修复
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
审计修复 Batch 2 (M4-03/M7-04/M11-01):
M4-03: 心跳引擎自动启动
- chat.rs auto-init 块: engine 创建后立即 start()
- 通过 engines.get() 获取引用避免 move 后使用
M7-04: refreshToken 发送 body 修复
- SaaSClient 新增 refreshTokenValue 存储 refresh_token
- refreshToken() 发送 { refresh_token } body
- SaaSRefreshResponse 新增 refresh_token 字段
- login/register 自动存储 refresh_token
- 添加 getRefreshToken/setRefreshToken 访问器
M11-01: blocking_lock 死锁修复 (已存在)
- 确认 try_lock + Result 匹配模式已正确
This commit is contained in:
@@ -137,10 +137,12 @@ pub async fn classroom_generate(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper: check if cancelled
|
// Helper: check if cancelled (try_lock avoids blocking the async runtime)
|
||||||
let is_cancelled = || {
|
let is_cancelled = || {
|
||||||
let t = tasks.blocking_lock();
|
match tasks.try_lock() {
|
||||||
!t.contains_key(&topic_clone)
|
Ok(t) => !t.contains_key(&topic_clone),
|
||||||
|
Err(_) => false, // Lock contested — treat as not cancelled
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper: emit progress event
|
// Helper: emit progress event
|
||||||
|
|||||||
@@ -163,7 +163,11 @@ pub async fn agent_chat_stream(
|
|||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
engines.insert(request.agent_id.clone(), engine);
|
engines.insert(request.agent_id.clone(), engine);
|
||||||
tracing::info!("[agent_chat_stream] Auto-initialized heartbeat for agent: {}", request.agent_id);
|
// Start the engine after insertion via the stored reference
|
||||||
|
if let Some(e) = engines.get(&request.agent_id) {
|
||||||
|
e.start().await;
|
||||||
|
}
|
||||||
|
tracing::info!("[agent_chat_stream] Auto-initialized and started heartbeat for agent: {}", request.agent_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,15 +20,15 @@ export function installAuthMethods(ClientClass: { prototype: any }): void {
|
|||||||
* Login with username and password.
|
* Login with username and password.
|
||||||
* Auto-sets the client token on success.
|
* Auto-sets the client token on success.
|
||||||
*/
|
*/
|
||||||
proto.login = async function (this: { token: string | null; request<T>(method: string, path: string, body?: unknown): Promise<T> }, username: string, password: string, totpCode?: string): Promise<SaaSLoginResponse> {
|
proto.login = async function (this: { token: string | null; refreshTokenValue: string | null; request<T>(method: string, path: string, body?: unknown): Promise<T> }, username: string, password: string, totpCode?: string): Promise<SaaSLoginResponse> {
|
||||||
const body: Record<string, string> = { username, password };
|
const body: Record<string, string> = { username, password };
|
||||||
if (totpCode) body.totp_code = totpCode;
|
if (totpCode) body.totp_code = totpCode; // Clear stale token before login — avoid sending expired token on auth endpoint
|
||||||
// Clear stale token before login — avoid sending expired token on auth endpoint
|
|
||||||
this.token = null;
|
this.token = null;
|
||||||
const data = await this.request<SaaSLoginResponse>(
|
const data = await this.request<SaaSLoginResponse>(
|
||||||
'POST', '/api/v1/auth/login', body,
|
'POST', '/api/v1/auth/login', body,
|
||||||
);
|
);
|
||||||
this.token = data.token;
|
this.token = data.token;
|
||||||
|
this.refreshTokenValue = data.refresh_token;
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ export function installAuthMethods(ClientClass: { prototype: any }): void {
|
|||||||
* Register a new account.
|
* Register a new account.
|
||||||
* Auto-sets the client token on success.
|
* Auto-sets the client token on success.
|
||||||
*/
|
*/
|
||||||
proto.register = async function (this: { token: string | null; request<T>(method: string, path: string, body?: unknown): Promise<T> }, data: {
|
proto.register = async function (this: { token: string | null; refreshTokenValue: string | null; request<T>(method: string, path: string, body?: unknown): Promise<T> }, data: {
|
||||||
username: string;
|
username: string;
|
||||||
email: string;
|
email: string;
|
||||||
password: string;
|
password: string;
|
||||||
@@ -48,6 +48,7 @@ export function installAuthMethods(ClientClass: { prototype: any }): void {
|
|||||||
'POST', '/api/v1/auth/register', data,
|
'POST', '/api/v1/auth/register', data,
|
||||||
);
|
);
|
||||||
this.token = result.token;
|
this.token = result.token;
|
||||||
|
this.refreshTokenValue = result.refresh_token;
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -62,9 +63,17 @@ export function installAuthMethods(ClientClass: { prototype: any }): void {
|
|||||||
* Refresh the current token.
|
* Refresh the current token.
|
||||||
* Auto-updates the client token on success.
|
* Auto-updates the client token on success.
|
||||||
*/
|
*/
|
||||||
proto.refreshToken = async function (this: { token: string | null; request<T>(method: string, path: string, body?: unknown): Promise<T> }): Promise<string> {
|
proto.refreshToken = async function (this: { token: string | null; refreshTokenValue: string | null; request<T>(method: string, path: string, body?: unknown): Promise<T> }): Promise<string> {
|
||||||
const data = await this.request<SaaSRefreshResponse>('POST', '/api/v1/auth/refresh');
|
if (!this.refreshTokenValue) {
|
||||||
|
throw new Error('No refresh token available');
|
||||||
|
}
|
||||||
|
const data = await this.request<SaaSRefreshResponse>('POST', '/api/v1/auth/refresh', {
|
||||||
|
refresh_token: this.refreshTokenValue,
|
||||||
|
});
|
||||||
this.token = data.token;
|
this.token = data.token;
|
||||||
|
if (data.refresh_token) {
|
||||||
|
this.refreshTokenValue = data.refresh_token;
|
||||||
|
}
|
||||||
return data.token;
|
return data.token;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ export type {
|
|||||||
export class SaaSClient {
|
export class SaaSClient {
|
||||||
private baseUrl: string;
|
private baseUrl: string;
|
||||||
private token: string | null = null;
|
private token: string | null = null;
|
||||||
|
private refreshTokenValue: string | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refresh mutex: shared Promise to prevent concurrent token refresh.
|
* Refresh mutex: shared Promise to prevent concurrent token refresh.
|
||||||
@@ -172,6 +173,16 @@ export class SaaSClient {
|
|||||||
this.token = token;
|
this.token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Set or clear the refresh token (in-memory only, never persisted) */
|
||||||
|
setRefreshToken(token: string | null): void {
|
||||||
|
this.refreshTokenValue = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the current refresh token */
|
||||||
|
getRefreshToken(): string | null {
|
||||||
|
return this.refreshTokenValue;
|
||||||
|
}
|
||||||
|
|
||||||
/** Check if the client is authenticated (token in memory or cookie-based) */
|
/** Check if the client is authenticated (token in memory or cookie-based) */
|
||||||
isAuthenticated(): boolean {
|
isAuthenticated(): boolean {
|
||||||
return !!this.token || this._cookieAuth;
|
return !!this.token || this._cookieAuth;
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ export interface SaaSLoginResponse {
|
|||||||
/** Refresh response from POST /api/v1/auth/refresh */
|
/** Refresh response from POST /api/v1/auth/refresh */
|
||||||
export interface SaaSRefreshResponse {
|
export interface SaaSRefreshResponse {
|
||||||
token: string;
|
token: string;
|
||||||
|
refresh_token: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** TOTP setup response from POST /api/v1/auth/totp/setup */
|
/** TOTP setup response from POST /api/v1/auth/totp/setup */
|
||||||
|
|||||||
Reference in New Issue
Block a user