Merge pull request 'feat: 优化对话历史功能、增加首页登录页面' (#155) from feat/historychat into develop

Reviewed-on: http://8.161.227.145:3000/XEngineers/CamTalk/pulls/155
This commit was merged in pull request #155.
This commit is contained in:
2026-06-20 20:06:39 +08:00
20 changed files with 2397 additions and 178 deletions

View File

@@ -208,3 +208,96 @@ export async function logout(
body: JSON.stringify({ refresh_token: refreshTokenStr }),
});
}
// ---- Conversation API ----
export interface ConversationListItem {
id: string;
title: string;
last_message: string;
message_count: number;
created_at: string;
updated_at: string;
}
export interface ConversationListResponse {
conversations: ConversationListItem[];
total: number;
page: number;
size: number;
}
export interface CreateConversationResponse {
id: string;
title: string;
created_at: string;
updated_at: string;
}
export interface StoredMessage {
id: number;
role: string;
content: string;
tokens_used: number;
created_at: string;
}
export interface MessagesResponse {
messages: StoredMessage[];
total: number;
}
export async function listConversations(
token: string,
page = 1,
size = 50
): Promise<ApiResponse<ConversationListResponse>> {
return request<ConversationListResponse>(
`/conversations?page=${page}&size=${size}`,
{ headers: authHeaders(token) }
);
}
export async function createConversation(
token: string,
config?: Record<string, unknown>
): Promise<ApiResponse<CreateConversationResponse>> {
return request<CreateConversationResponse>("/conversations", {
method: "POST",
headers: authHeaders(token),
body: JSON.stringify(config ? { config } : {}),
});
}
export async function deleteConversation(
token: string,
id: string
): Promise<ApiResponse<void>> {
return request<void>(`/conversations/${id}`, {
method: "DELETE",
headers: authHeaders(token),
});
}
export async function renameConversation(
token: string,
id: string,
title: string
): Promise<ApiResponse<{ message: string }>> {
return request<{ message: string }>(`/conversations/${id}`, {
method: "PATCH",
headers: authHeaders(token),
body: JSON.stringify({ title }),
});
}
export async function getConversationMessages(
token: string,
id: string,
limit = 200
): Promise<ApiResponse<MessagesResponse>> {
return request<MessagesResponse>(
`/conversations/${id}/messages?limit=${limit}`,
{ headers: authHeaders(token) }
);
}

View File

@@ -25,6 +25,7 @@ export class CamTalkWebSocket {
private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
private shouldReconnect = true;
private token: string | undefined;
private conversationId: string | undefined;
private messageHandlers = new Set<MessageHandler>();
private statusHandlers = new Set<StatusHandler>();
@@ -46,15 +47,20 @@ export class CamTalkWebSocket {
return () => this.statusHandlers.delete(handler);
}
/** 建立连接,可选传入 JWT token 用于认证 */
connect(token?: string): void {
/** 建立连接,可选传入 JWT token 和 conversation_id 用于认证和会话恢复 */
connect(token?: string, conversationId?: string): void {
if (this.ws?.readyState === WebSocket.OPEN) return;
this.token = token;
this.conversationId = conversationId;
this.shouldReconnect = true;
this.setStatus("connecting");
const url = token ? `${WS_URL}?token=${encodeURIComponent(token)}` : WS_URL;
const params = new URLSearchParams();
if (token) params.set("token", token);
if (conversationId) params.set("conversation_id", conversationId);
const queryString = params.toString();
const url = queryString ? `${WS_URL}?${queryString}` : WS_URL;
const ws = new WebSocket(url);
ws.onopen = () => {
@@ -133,7 +139,7 @@ export class CamTalkWebSocket {
this.reconnectTimer = setTimeout(() => {
this.reconnectAttempt++;
this.connect(this.token);
this.connect(this.token, this.conversationId);
}, totalDelay);
}