64 lines
2.1 KiB
TypeScript
64 lines
2.1 KiB
TypeScript
// 图片上传与短时签名 URL (与 server/routes/images.js 对应)
|
|
import { API_BASE } from "./config";
|
|
import { getToken } from "./index";
|
|
|
|
function base(p: string) {
|
|
return API_BASE ? API_BASE + p : p;
|
|
}
|
|
|
|
function authHeaders(): Record<string, string> {
|
|
const t = getToken();
|
|
return t ? { Authorization: `Bearer ${t}` } : {};
|
|
}
|
|
|
|
async function request<T>(method: string, path: string, body?: unknown): Promise<T> {
|
|
const headers: Record<string, string> = { "Content-Type": "application/json", ...authHeaders() };
|
|
let res: Response;
|
|
try {
|
|
res = await fetch(base(path), { method, headers, body: body === undefined ? undefined : JSON.stringify(body) });
|
|
} catch {
|
|
throw new Error("网络错误,请检查网络连接");
|
|
}
|
|
let data: unknown = null;
|
|
try {
|
|
data = await res.json();
|
|
} catch { /* no body */ }
|
|
if (!res.ok) {
|
|
const msg = (data as { error?: string } | null)?.error ?? `请求失败 (${res.status})`;
|
|
if (res.status === 401) localStorage.removeItem("storage_user");
|
|
throw new Error(msg);
|
|
}
|
|
return data as T;
|
|
}
|
|
|
|
// 上传: multipart/form-data, 返回暂存文件名 (挂到物品后由服务端归入家庭目录)
|
|
export async function uploadImage(file: File): Promise<string> {
|
|
const fd = new FormData();
|
|
fd.append("file", file);
|
|
let res: Response;
|
|
try {
|
|
res = await fetch(base("/api/images"), { method: "POST", headers: authHeaders(), body: fd });
|
|
} catch {
|
|
throw new Error("网络错误,请检查网络连接");
|
|
}
|
|
let data: unknown = null;
|
|
try {
|
|
data = await res.json();
|
|
} catch { /* no body */ }
|
|
if (!res.ok) {
|
|
const msg = (data as { error?: string } | null)?.error ?? `上传失败 (${res.status})`;
|
|
throw new Error(msg);
|
|
}
|
|
return (data as { path: string }).path;
|
|
}
|
|
|
|
// 生成短时签名 URL (15 分钟), 供 <img> 直接使用
|
|
export function signImagePath(path: string) {
|
|
return request<{ url: string }>("POST", "/api/images/sign", { path });
|
|
}
|
|
|
|
// 带鉴权的输入框支持 (web 浏览器限制多, 移动端/桌面经 Tauri shell 无缝)
|
|
export function imageAuthHeader(): Record<string, string> {
|
|
const t = getToken();
|
|
return t ? { Authorization: `Bearer ${t}` } : {};
|
|
} |