first commit

This commit is contained in:
Beyhan Oğur
2026-04-26 22:16:43 +03:00
commit 6d95e27114
97 changed files with 15687 additions and 0 deletions

109
lib/api/fetchClient.ts Normal file
View File

@@ -0,0 +1,109 @@
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();
};