fix: unify frontend API client - add blob support to fetchWithAuth, eliminate raw fetch calls
- Extend fetchWithAuth with responseType parameter ('json' | 'blob')
- reports.ts: PDF/CSV export now uses fetchWithAuth blob mode
- reports/page.tsx: remove duplicate API_BASE, use fetchWithAuth for CSV export
- lifecycle/new/page.tsx: replace raw fetch with fetchWithAuth for quick-start POST
This commit is contained in:
parent
792d9ebe53
commit
f1a8b69c2a
|
|
@ -7,6 +7,7 @@ import { Button } from "@/components/ui/button";
|
|||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { AlertCard } from "@/components/business/alert-card";
|
||||
import { fetchWithAuth } from "@/lib/api/client";
|
||||
import { Check } from "lucide-react";
|
||||
|
||||
interface ProjectResponse {
|
||||
|
|
@ -57,39 +58,20 @@ export default function NewProjectPage() {
|
|||
const token = session?.accessToken as string | undefined;
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000"}/api/v1/lifecycle/projects/quick-start`,
|
||||
const data = await fetchWithAuth(
|
||||
"/api/v1/lifecycle/projects/quick-start",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token || ""}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
brand_name: brandName.trim(),
|
||||
brand_url: brandUrl.trim(),
|
||||
description: description.trim() || undefined,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
let errorMessage = "请求失败,请稍后重试";
|
||||
try {
|
||||
const errData = await response.json();
|
||||
errorMessage = errData.detail || errData.message || errorMessage;
|
||||
} catch {
|
||||
// ignore parse error
|
||||
}
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const _data = (await response.json()) as {
|
||||
project: ProjectResponse;
|
||||
message: string;
|
||||
};
|
||||
},
|
||||
token
|
||||
) as { project: ProjectResponse; message: string };
|
||||
|
||||
void data;
|
||||
animateSteps();
|
||||
} catch (err) {
|
||||
setSubmitting(false);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import {
|
|||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { reportsApi } from "@/lib/api/reports";
|
||||
import { fetchWithAuth } from "@/lib/api/client";
|
||||
import type { QueryListResponse } from "@/lib/api/queries";
|
||||
import type { CitationListResponse, CitationStats } from "@/lib/api/citations";
|
||||
import { useApi } from "@/lib/hooks/use-api";
|
||||
|
|
@ -39,8 +40,6 @@ import {
|
|||
BarChart3,
|
||||
} from "lucide-react";
|
||||
|
||||
const API_BASE = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000";
|
||||
|
||||
export default function ReportsPage() {
|
||||
const { data: session } = useSession();
|
||||
const [selectedQuery, setSelectedQuery] = useState<string>("");
|
||||
|
|
@ -87,15 +86,7 @@ export default function ReportsPage() {
|
|||
|
||||
if (format === "csv") {
|
||||
const query = `?query_id=${queryId}`;
|
||||
const url = `${API_BASE}/api/v1/reports/export/csv${query}`;
|
||||
const res = await fetch(url, {
|
||||
headers: { Authorization: `Bearer ${session.accessToken}` },
|
||||
});
|
||||
if (!res.ok) {
|
||||
const errorData = await res.json().catch(() => ({ detail: "导出失败" }));
|
||||
throw new Error(errorData.detail || `HTTP ${res.status}`);
|
||||
}
|
||||
blob = await res.blob();
|
||||
blob = await fetchWithAuth(`/api/v1/reports/export/csv${query}`, {}, session.accessToken, "blob") as unknown as Blob;
|
||||
filename = `report_${queryId}_${new Date().toISOString().split("T")[0]}.csv`;
|
||||
} else {
|
||||
blob = await reportsApi.exportPDF(session.accessToken, queryId);
|
||||
|
|
|
|||
|
|
@ -7,12 +7,14 @@ export function getApiUrl(path: string): string {
|
|||
return `${API_BASE}${path}`;
|
||||
}
|
||||
|
||||
export type ResponseType = "json" | "blob";
|
||||
|
||||
export async function fetchWithAuth(
|
||||
url: string,
|
||||
options: RequestInit = {},
|
||||
token?: string
|
||||
token?: string,
|
||||
responseType: ResponseType = "json"
|
||||
) {
|
||||
// 如果没有显式传入 token,尝试从 NextAuth session 获取
|
||||
let authToken = token;
|
||||
if (!authToken && typeof window !== "undefined") {
|
||||
try {
|
||||
|
|
@ -33,7 +35,6 @@ export async function fetchWithAuth(
|
|||
const res = await fetch(`${API_BASE}${url}`, { ...options, headers });
|
||||
|
||||
if (res.status === 401) {
|
||||
// 不要自动跳转登录页,让页面组件/layout自行处理认证状态
|
||||
throw new Error("登录已过期,请重新登录");
|
||||
}
|
||||
|
||||
|
|
@ -52,5 +53,9 @@ export async function fetchWithAuth(
|
|||
return null;
|
||||
}
|
||||
|
||||
if (responseType === "blob") {
|
||||
return res.blob();
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
import { API_BASE, fetchWithAuth } from "./client";
|
||||
import { fetchWithAuth } from "./client";
|
||||
|
||||
export const reportsApi = {
|
||||
exportCSV: (token: string, queryId?: string) => {
|
||||
const query = queryId ? `?query_id=${queryId}` : "";
|
||||
return fetchWithAuth(`/api/v1/reports/export/csv${query}`, {}, token);
|
||||
return fetchWithAuth(`/api/v1/reports/export/csv${query}`, {}, token, "blob");
|
||||
},
|
||||
exportPDF: async (token: string, queryId?: string) => {
|
||||
const query = queryId ? `?query_id=${queryId}` : "";
|
||||
const res = await fetch(`${API_BASE}/api/v1/reports/export/pdf${query}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
if (!res.ok) throw new Error("导出失败");
|
||||
return res.blob();
|
||||
return fetchWithAuth(`/api/v1/reports/export/pdf${query}`, {}, token, "blob");
|
||||
},
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue