feat: scaffold Vue3 Element Plus frontend with mobile base
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
export type TTLOption = '1h' | '24h' | '7d' | 'never'
|
||||
|
||||
export interface Item {
|
||||
slug: string
|
||||
kind: string
|
||||
title: string
|
||||
body?: string | null
|
||||
file_name?: string | null
|
||||
mime?: string | null
|
||||
size_bytes?: number | null
|
||||
is_public: boolean
|
||||
burn_after_read: boolean
|
||||
expires_at: string | null
|
||||
created_at: string
|
||||
view_count: number
|
||||
id?: number
|
||||
created_ip?: string
|
||||
burned?: boolean
|
||||
}
|
||||
|
||||
export interface WallResponse {
|
||||
items: Item[]
|
||||
page: number
|
||||
page_size: number
|
||||
total: number
|
||||
}
|
||||
|
||||
export interface Ban {
|
||||
ip: string
|
||||
reason: string
|
||||
created_at: string
|
||||
}
|
||||
|
||||
async function parseError(res: Response): Promise<string> {
|
||||
try {
|
||||
const data = await res.json()
|
||||
if (typeof data?.detail === 'string') return data.detail
|
||||
return JSON.stringify(data?.detail ?? data)
|
||||
} catch {
|
||||
return res.statusText || `HTTP ${res.status}`
|
||||
}
|
||||
}
|
||||
|
||||
async function request<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
const res = await fetch(path, init)
|
||||
if (!res.ok) {
|
||||
throw new Error(await parseError(res))
|
||||
}
|
||||
if (res.status === 204) {
|
||||
return undefined as T
|
||||
}
|
||||
return (await res.json()) as T
|
||||
}
|
||||
|
||||
export function createItem(payload: {
|
||||
body: string
|
||||
title?: string
|
||||
is_public: boolean
|
||||
burn_after_read: boolean
|
||||
ttl: TTLOption
|
||||
}): Promise<Item> {
|
||||
return request<Item>('/api/items', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
}
|
||||
|
||||
export function uploadItem(form: FormData): Promise<Item> {
|
||||
return request<Item>('/api/items/upload', {
|
||||
method: 'POST',
|
||||
body: form,
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchWall(page = 1, pageSize = 20): Promise<WallResponse> {
|
||||
const q = new URLSearchParams({
|
||||
page: String(page),
|
||||
page_size: String(pageSize),
|
||||
})
|
||||
return request<WallResponse>(`/api/items/wall?${q}`)
|
||||
}
|
||||
|
||||
export function fetchItem(slug: string): Promise<Item> {
|
||||
return request<Item>(`/api/items/${encodeURIComponent(slug)}`)
|
||||
}
|
||||
|
||||
export function itemFileUrl(slug: string): string {
|
||||
return `/api/items/${encodeURIComponent(slug)}/file`
|
||||
}
|
||||
|
||||
export function adminLogin(password: string): Promise<{ token: string }> {
|
||||
return request<{ token: string }>('/api/admin/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ password }),
|
||||
})
|
||||
}
|
||||
|
||||
function authHeaders(token: string): HeadersInit {
|
||||
return { Authorization: `Bearer ${token}` }
|
||||
}
|
||||
|
||||
export function adminListItems(token: string): Promise<{ items: Item[] }> {
|
||||
return request<{ items: Item[] }>('/api/admin/items', {
|
||||
headers: authHeaders(token),
|
||||
})
|
||||
}
|
||||
|
||||
export function adminDeleteItem(token: string, itemId: number): Promise<{ ok: boolean }> {
|
||||
return request<{ ok: boolean }>(`/api/admin/items/${itemId}`, {
|
||||
method: 'DELETE',
|
||||
headers: authHeaders(token),
|
||||
})
|
||||
}
|
||||
|
||||
export function adminListBans(token: string): Promise<{ bans: Ban[] }> {
|
||||
return request<{ bans: Ban[] }>('/api/admin/bans', {
|
||||
headers: authHeaders(token),
|
||||
})
|
||||
}
|
||||
|
||||
export function adminCreateBan(
|
||||
token: string,
|
||||
payload: { ip: string; reason?: string },
|
||||
): Promise<{ ok: boolean }> {
|
||||
return request<{ ok: boolean }>('/api/admin/bans', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
...authHeaders(token),
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
}
|
||||
|
||||
export function adminDeleteBan(token: string, ip: string): Promise<{ ok: boolean }> {
|
||||
return request<{ ok: boolean }>(`/api/admin/bans/${encodeURIComponent(ip)}`, {
|
||||
method: 'DELETE',
|
||||
headers: authHeaders(token),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user