/
som_cooper
/
book-lib
Обзор
Документация
Войти
/
som_cooper
/
book-lib
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
admin/src/lib/api.js
98 строк
3 KB
imdbcooper
chore: harden admin auth and imports
25 май 2026, 18:07
25 май 2026, 18:07
54aed78
Код
Авторство
О чём код?
const API_BASE = (import.meta.env.VITE_API_BASE_URL || '').replace(/\/$/, '') export function buildUrl(path) { return API_BASE ? `${API_BASE}${path}` : path } export function buildAssetUrl(path) { if (!path) return '' if (/^https?:\/\//i.test(path)) return path return buildUrl(path) } const STATE_CHANGING_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE']) let csrfTokenPromise = null function normalizeMethod(method) { return String(method || 'GET').toUpperCase() } function shouldAttachCsrf(path, method) { if (!STATE_CHANGING_METHODS.has(method)) return false if (!String(path).startsWith('/api/admin/')) return false return !['/api/admin/login', '/api/admin/logout'].includes(String(path)) } async function getCsrfToken({ forceRefresh = false } = {}) { if (!csrfTokenPromise || forceRefresh) { csrfTokenPromise = fetch(buildUrl('/api/admin/csrf'), { credentials: 'include', }) .then(async (response) => { const data = await response.json().catch(() => ({})) if (!response.ok || !data.csrfToken) { throw new Error(data.error || 'Не удалось получить CSRF token') } return data.csrfToken }) .catch((error) => { csrfTokenPromise = null throw error }) } return csrfTokenPromise } export function clearCsrfToken() { csrfTokenPromise = null } export async function apiFetch(path, options = {}) { const method = normalizeMethod(options.method) const headers = new Headers(options.headers || {}) if (shouldAttachCsrf(path, method) && !headers.has('X-CSRF-Token')) { headers.set('X-CSRF-Token', await getCsrfToken()) } let response = await fetch(buildUrl(path), { credentials: 'include', ...options, method, headers, }) if (response.status === 403 && shouldAttachCsrf(path, method)) { headers.set('X-CSRF-Token', await getCsrfToken({ forceRefresh: true })) response = await fetch(buildUrl(path), { credentials: 'include', ...options, method, headers, }) } return response } export async function apiJson(path, options = {}) { const headers = new Headers(options.headers || {}) // Автоматически добавляем Content-Type для JSON body if (options.body && typeof options.body === 'string' && !headers.has('Content-Type')) { headers.set('Content-Type', 'application/json') } const response = await apiFetch(path, { ...options, headers, }) const data = await response.json().catch(() => ({})) if (!response.ok) { throw new Error(data.error || 'Не удалось выполнить запрос') } return data }