Files
WebDAV/web/src/api.ts
T
Yunling Zhang 73f253581a feat(client): 新增 GET /api/client/me,刷新后显示当前账号名
- 会话内返回账号名与只读标记,不含口令
- client 页加载列表后拉取 me,顶栏展示账号名,刷新不丢失
2026-09-28 17:38:20 +08:00

118 lines
3.6 KiB
TypeScript

export interface AccountView {
user: string
root: string
readonly: boolean
disabled: boolean
note: string
usedBytes: number
usedFiles: number
}
export interface ConnInfo {
url: string
user: string
pass: string
}
export interface Entry {
name: string
isDir: boolean
size: number
mtime: number
}
class ApiError extends Error {
status: number
constructor(status: number, message: string) {
super(message)
this.status = status
}
}
async function request<T>(method: string, url: string, body?: unknown): Promise<T> {
const init: RequestInit = { method, credentials: 'same-origin' }
if (body !== undefined) {
init.headers = { 'Content-Type': 'application/json' }
init.body = JSON.stringify(body)
}
const res = await fetch(url, init)
const text = await res.text()
let data: unknown = null
if (text) {
try {
data = JSON.parse(text)
} catch {
data = null
}
}
if (!res.ok) {
const message =
data && typeof data === 'object' && 'error' in data
? String((data as { error: unknown }).error)
: '请求失败'
throw new ApiError(res.status, message)
}
return data as T
}
export { ApiError }
export const adminApi = {
login: (password: string) => request<{ ok: boolean }>('POST', '/api/admin/login', { password }),
logout: () => request<{ ok: boolean }>('POST', '/api/admin/logout'),
list: () => request<AccountView[]>('GET', '/api/admin/accounts'),
create: (user: string, readonly: boolean, note: string) =>
request<{ user: string; pass: string; url: string }>('POST', '/api/admin/accounts', {
user,
readonly,
note,
}),
conn: (user: string) => request<ConnInfo>('GET', `/api/admin/accounts/${encodeURIComponent(user)}/conn`),
rotate: (user: string) =>
request<{ user: string; pass: string; url: string }>(
'POST',
`/api/admin/accounts/${encodeURIComponent(user)}/rotate`,
),
patch: (user: string, patch: { readonly?: boolean; disabled?: boolean; note?: string }) =>
request<AccountView>('PATCH', `/api/admin/accounts/${encodeURIComponent(user)}`, patch),
remove: (user: string) =>
request<{ ok: boolean; message: string }>('DELETE', `/api/admin/accounts/${encodeURIComponent(user)}`),
}
function encodePath(p: string): string {
return p
.split('/')
.map((seg) => encodeURIComponent(seg))
.join('/')
}
export const clientApi = {
login: (user: string, pass: string) =>
request<{ ok: boolean; user: string }>('POST', '/api/client/login', { user, pass }),
logout: () => request<{ ok: boolean }>('POST', '/api/client/logout'),
me: () => request<{ user: string; readonly: boolean }>('GET', '/api/client/me'),
list: (path: string) => request<Entry[]>('GET', `/api/client/list?path=${encodeURIComponent(path)}`),
mkdir: (path: string) => request<{ ok: boolean }>('POST', '/api/client/mkdir', { path }),
rename: (from: string, to: string) => request<{ ok: boolean }>('POST', '/api/client/rename', { from, to }),
remove: (path: string) =>
request<{ ok: boolean }>('DELETE', `/api/client/entry?path=${encodeURIComponent(path)}`),
upload: async (target: string, file: File): Promise<void> => {
const res = await fetch(`/api/client/raw${encodePath(target)}`, {
method: 'PUT',
credentials: 'same-origin',
body: file,
})
if (!res.ok) {
let message = '上传失败'
try {
const data = (await res.json()) as { error?: string }
if (data.error) message = data.error
} catch {
/* 保持默认文案 */
}
throw new ApiError(res.status, message)
}
},
rawURL: (path: string) => `/api/client/raw${encodePath(path)}`,
}