add feed in / out button page and also button to redirect to button page
This commit is contained in:
1 parent
07b8e2dc25
commit
b86bd7a2f4
17 files changed
+719
-28
No files matched your search
+17
-5
@@ -55,7 +55,12 @@ function getCookie(name: string): string | null {
|
||||
function parseDetail(body: unknown): string {
|
||||
if (!body || typeof body !== 'object') return 'Request failed';
|
||||
const detail = (body as { detail?: unknown }).detail;
|
||||
if (typeof detail === 'string') return detail;
|
||||
if (typeof detail === 'string') {
|
||||
if (detail.toLowerCase().includes('csrf')) {
|
||||
return 'Permintaan ditolak (CSRF). Muat ulang halaman lalu coba lagi.';
|
||||
}
|
||||
return detail;
|
||||
}
|
||||
if (Array.isArray(detail)) return detail.map(String).join(', ');
|
||||
const entries = Object.entries(body as Record<string, unknown>)
|
||||
.filter(([key]) => key !== 'detail')
|
||||
@@ -64,8 +69,6 @@ function parseDetail(body: unknown): string {
|
||||
}
|
||||
|
||||
async function ensureCsrf(): Promise<string> {
|
||||
const existing = getCookie('csrftoken');
|
||||
if (existing) return existing;
|
||||
const res = await fetch(`${API_BASE}/auth/csrf/`, { credentials: 'include' });
|
||||
if (!res.ok) {
|
||||
throw new ApiError('Gagal mengambil CSRF token', res.status, null);
|
||||
@@ -74,6 +77,12 @@ async function ensureCsrf(): Promise<string> {
|
||||
return data.csrfToken || getCookie('csrftoken') || '';
|
||||
}
|
||||
|
||||
function isCsrfFailure(status: number, body: unknown): boolean {
|
||||
if (status !== 403) return false;
|
||||
const detail = parseDetail(body).toLowerCase();
|
||||
return detail.includes('csrf');
|
||||
}
|
||||
|
||||
type Query = Record<string, string | number | boolean | null | undefined>;
|
||||
|
||||
function withQuery(path: string, query?: Query): string {
|
||||
@@ -87,7 +96,7 @@ function withQuery(path: string, query?: Query): string {
|
||||
return qs ? `${path}?${qs}` : path;
|
||||
}
|
||||
|
||||
async function request<T>(path: string, init: RequestInit = {}): Promise<T> {
|
||||
async function request<T>(path: string, init: RequestInit = {}, csrfRetried = false): Promise<T> {
|
||||
const method = (init.method || 'GET').toUpperCase();
|
||||
const headers = new Headers(init.headers);
|
||||
headers.set('Accept', 'application/json');
|
||||
@@ -121,7 +130,10 @@ async function request<T>(path: string, init: RequestInit = {}): Promise<T> {
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
if (res.status === 401 || res.status === 403) {
|
||||
if (isCsrfFailure(res.status, body) && !csrfRetried && method !== 'GET' && method !== 'HEAD') {
|
||||
return request<T>(path, init, true);
|
||||
}
|
||||
if (res.status === 401) {
|
||||
window.dispatchEvent(new CustomEvent(AUTH_UNAUTHORIZED_EVENT));
|
||||
}
|
||||
throw new ApiError(parseDetail(body), res.status, body);
|
||||
|
||||
Reference in new issue
Block a user