78e5c90ed0
also small various features
81 lines
2.3 KiB
TypeScript
81 lines
2.3 KiB
TypeScript
const RETRY_DELAYS = [0, 500, 1500]
|
|
const CACHE_TTL_MS = 24 * 60 * 60 * 1000
|
|
|
|
function cacheKey(url: string) {
|
|
return `nids_cache:${url}`
|
|
}
|
|
|
|
function saveToCache(url: string, data: unknown) {
|
|
try {
|
|
localStorage.setItem(cacheKey(url), JSON.stringify({ data, ts: Date.now() }))
|
|
} catch {}
|
|
}
|
|
|
|
function loadFromCache<T>(url: string): T | null {
|
|
try {
|
|
const raw = localStorage.getItem(cacheKey(url))
|
|
if (!raw) return null
|
|
const { data, ts } = JSON.parse(raw)
|
|
if (Date.now() - ts > CACHE_TTL_MS) return null
|
|
return data as T
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
// Базовый fetch к Go API
|
|
export function useApi() {
|
|
async function get<T>(path: string, params?: Record<string, string | number>): Promise<T> {
|
|
const url = new URL('/api' + path, window.location.origin)
|
|
if (params) {
|
|
Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, String(v)))
|
|
}
|
|
const urlStr = url.toString()
|
|
|
|
let lastErr: unknown
|
|
for (let i = 0; i < RETRY_DELAYS.length; i++) {
|
|
if (i > 0) await new Promise(r => setTimeout(r, RETRY_DELAYS[i]))
|
|
try {
|
|
const res = await fetch(urlStr, { signal: AbortSignal.timeout(5000) })
|
|
if (!res.ok) throw new Error(await res.text())
|
|
const data = await res.json() as T
|
|
saveToCache(urlStr, data)
|
|
return data
|
|
} catch (e) {
|
|
lastErr = e
|
|
}
|
|
}
|
|
|
|
const cached = loadFromCache<T>(urlStr)
|
|
if (cached !== null) return cached
|
|
|
|
throw lastErr
|
|
}
|
|
|
|
async function post<T>(path: string, body?: unknown) {
|
|
const res = await fetch('/api' + path, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: body ? JSON.stringify(body) : undefined,
|
|
})
|
|
if (!res.ok) throw new Error(await res.text())
|
|
return res.json() as Promise<T>
|
|
}
|
|
|
|
async function del(path: string) {
|
|
const res = await fetch('/api' + path, { method: 'DELETE' })
|
|
if (!res.ok) throw new Error(await res.text())
|
|
return res.json()
|
|
}
|
|
|
|
async function upload(path: string, file: File) {
|
|
const form = new FormData()
|
|
form.append('file', file)
|
|
const res = await fetch('/api' + path, { method: 'POST', body: form })
|
|
if (!res.ok) throw new Error(await res.text())
|
|
return res.json()
|
|
}
|
|
|
|
return { get, post, del, upload }
|
|
}
|