feat(web): 历史解卦列表、结果页与追问功能
- 合并 DivinationResultPage 和 HistoryResultPage 为统一结果页 - 重写 HistoryFollowUpPage:API 加载历史消息、SSE 流式追问、配额管理 - 追问免费且限一次,输入框 UI 对齐设计稿(圆角容器+配额徽章+圆形发送按钮) - 结果页追问状态根据线程消息数动态判断 - 历史列表筛选改为 9 类独立类型 - 提取 historyMessageToResultData 为共享函数,新增 enqueueFollowUpRun API - 新增 auto_awesome/search/arrow_upward 图标 - 新增三语言 [id].astro、[id]/followup.astro、divination/result.astro 页面
This commit is contained in:
@@ -237,3 +237,39 @@ export async function authFetch<T>(path: string, options?: RequestInit): Promise
|
||||
if (res.status === 204) return undefined as T;
|
||||
return res.json() as Promise<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like authFetch but returns raw Response for streaming (SSE, etc.)
|
||||
* Does NOT throw on non-OK responses - caller must handle response.status
|
||||
*/
|
||||
export async function authFetchRaw(path: string, options?: RequestInit): Promise<Response> {
|
||||
if (isTokenExpired()) {
|
||||
await refreshAccessToken();
|
||||
}
|
||||
|
||||
const auth = getAuth();
|
||||
if (!auth) {
|
||||
redirectToLogin();
|
||||
throw new Error('Not authenticated');
|
||||
}
|
||||
|
||||
const headers = jsonHeaders(options);
|
||||
headers.set('Authorization', `Bearer ${auth.access_token}`);
|
||||
|
||||
const url = apiUrl(path);
|
||||
let res = await fetch(url, { ...options, headers });
|
||||
|
||||
if (res.status === 401) {
|
||||
await refreshAccessToken();
|
||||
const refreshed = getAuth();
|
||||
if (!refreshed) {
|
||||
redirectToLogin();
|
||||
throw new Error('Not authenticated');
|
||||
}
|
||||
const retryHeaders = jsonHeaders(options);
|
||||
retryHeaders.set('Authorization', `Bearer ${refreshed.access_token}`);
|
||||
res = await fetch(url, { ...options, headers: retryHeaders });
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user