PersonalWebApplication/Client/src/api/folders.ts

139 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 特殊文件夹图片接口。基址与 photos.ts 一致。
const API_BASE =
(import.meta.env.VITE_API_BASE as string | undefined) ?? 'http://localhost:8080'
function authHeaders(token: string): Record<string, string> {
return { Authorization: `Bearer ${token}` }
}
/** 检查文件夹是否已注册(登录前,无鉴权) */
export async function checkFolder(name: string): Promise<boolean> {
try {
const res = await fetch(`${API_BASE}/api/web/folder`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name }),
})
if (!res.ok) return false
return ((await res.json()) as { ok: boolean }).ok
} catch {
return false
}
}
export interface FolderPhotoItem {
id: string
}
export interface FolderPhotoList {
items: FolderPhotoItem[]
max?: number // 总数上限(从服务器 max 字段)
totalBytes: number
overQuota: boolean
}
/** 上传失败的可区分原因(同 photos.ts多 'full' 对应 409 */
export type UploadError = 'full' | 'too-large' | 'bad-type' | 'failed'
/** 列出文件夹内所有图片,含配额信息 */
export async function listFolderPhotos(
token: string,
folder: string,
): Promise<FolderPhotoList> {
const res = await fetch(`${API_BASE}/api/web/folder/${folder}/photos`, {
headers: authHeaders(token),
})
if (res.status === 403) throw new Error('forbidden')
if (!res.ok) throw new Error(`list failed: ${res.status}`)
return (await res.json()) as FolderPhotoList
}
/** 上传图片到文件夹,返回服务器分配的 id */
export async function uploadFolderPhoto(
token: string,
folder: string,
dataUrl: string,
): Promise<string> {
const res = await fetch(`${API_BASE}/api/web/folder/${folder}/photos`, {
method: 'POST',
headers: { ...authHeaders(token), 'Content-Type': 'application/json' },
body: JSON.stringify({ dataUrl }),
})
if (res.ok) return ((await res.json()) as { id: string }).id
if (res.status === 403) throw new Error('forbidden')
const reason: UploadError =
res.status === 409
? 'full'
: res.status === 413
? 'too-large'
: res.status === 415
? 'bad-type'
: 'failed'
throw new Error(reason)
}
/** 删除文件夹内图片 */
export async function deleteFolderPhoto(
token: string,
folder: string,
id: string,
): Promise<void> {
const res = await fetch(`${API_BASE}/api/web/folder/${folder}/photos/${id}`, {
method: 'DELETE',
headers: authHeaders(token),
})
if (res.status === 403) throw new Error('forbidden')
if (!res.ok && res.status !== 404) throw new Error(`delete failed: ${res.status}`)
}
/** 获取文件夹内图片的 objectURL调用方负责释放 */
export async function fetchFolderPhotoUrl(
token: string,
folder: string,
id: string,
): Promise<string> {
const res = await fetch(`${API_BASE}/api/web/folder/${folder}/photos/${id}`, {
headers: authHeaders(token),
})
if (res.status === 403) throw new Error('forbidden')
if (!res.ok) throw new Error(`get failed: ${res.status}`)
return URL.createObjectURL(await res.blob())
}
/* ── 照片墙状态(装饰 + 排版,服务器共享)────────────────────── */
export interface WallState {
bgIndex: number
decorations: Record<string, unknown>[]
mediaLayout?: Record<string, Record<string, unknown>>
photoLayout?: Record<string, Record<string, unknown>>
}
/** 读取文件夹的共享照片墙状态;无存档返回 null */
export async function fetchFolderState(
token: string,
folder: string,
): Promise<WallState | null> {
const res = await fetch(`${API_BASE}/api/web/folder/${folder}/state`, {
headers: authHeaders(token),
})
if (res.status === 404) return null
if (!res.ok) throw new Error(`get state failed: ${res.status}`)
return (await res.json()) as WallState
}
/** 保存文件夹的共享照片墙状态409 = 超 100 条上限) */
export async function saveFolderState(
token: string,
folder: string,
state: WallState,
): Promise<void> {
const res = await fetch(`${API_BASE}/api/web/folder/${folder}/state`, {
method: 'PUT',
headers: { ...authHeaders(token), 'Content-Type': 'application/json' },
body: JSON.stringify(state),
})
if (res.status === 409) throw new Error('full')
if (!res.ok) throw new Error(`save state failed: ${res.status}`)
}