Files
next-go-blog/lib/api/fetchClient.ts
Beyhan Oğur 6d95e27114 first commit
2026-04-26 22:16:43 +03:00

110 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Cookies from "js-cookie";
// .env'deki NEXT_PUBLIC_API_BASE kullanılır (örn: http://127.0.0.1:8080). /v1 prefix'i burada eklenir.
const getBaseUrl = () => {
const base = process.env.NEXT_PUBLIC_API_BASE || "http://localhost:8080";
const normalized = base.replace(/\/$/, "");
return `${normalized}/v1`;
};
const BASE_URL = getBaseUrl();
interface FetchOptions extends RequestInit {
headers?: Record<string, string>;
}
interface AuthResponse {
access_token: string;
refresh_token: string;
}
export const fetchClient = async (endpoint: string, options: FetchOptions = {}) => {
const getAccessToken = () => Cookies.get("access_token");
const getRefreshToken = () => Cookies.get("refresh_token");
const setTokens = (access: string, refresh: string) => {
Cookies.set("access_token", access, { secure: true, sameSite: 'strict' });
Cookies.set("refresh_token", refresh, { secure: true, sameSite: 'strict' });
// Dispatch event for other tabs or parts of the app to know (optional)
if (typeof window !== "undefined") {
window.dispatchEvent(new Event("storage"));
}
};
const clearTokens = () => {
Cookies.remove("access_token");
Cookies.remove("refresh_token");
if (typeof window !== "undefined") {
try {
localStorage.removeItem("user");
} catch (e) { }
window.location.href = "/login";
}
};
const headers: Record<string, string> = {
...options.headers,
};
if (!(options.body instanceof FormData)) {
headers["Content-Type"] = "application/json";
}
const token = getAccessToken();
if (token) {
headers["Authorization"] = `Bearer ${token}`;
}
const config: RequestInit = {
...options,
headers,
};
let response = await fetch(`${BASE_URL}${endpoint}`, config);
// Handle 401 - Token Expired
if (response.status === 401) {
const refreshToken = getRefreshToken();
if (!refreshToken) {
clearTokens();
throw new Error("Session expired");
}
try {
// Attempt to refresh token
const refreshResponse = await fetch(`${BASE_URL}/auth/refresh`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ refresh_token: refreshToken }),
});
if (!refreshResponse.ok) {
throw new Error("Refresh failed");
}
const data: AuthResponse = await refreshResponse.json();
setTokens(data.access_token, data.refresh_token);
// Retry original request with new token
headers["Authorization"] = `Bearer ${data.access_token}`;
response = await fetch(`${BASE_URL}${endpoint}`, { ...options, headers });
} catch (error) {
clearTokens();
throw new Error("Session expired. Please login again.");
}
}
// Handle other errors
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
const errorMessage = errorData.error || errorData.message || response.statusText;
throw new Error(errorMessage);
}
// Return json if content type is json, otherwise text or null
const contentType = response.headers.get("content-type");
if (contentType && contentType.includes("application/json")) {
return response.json();
}
return response.text();
};