177 lines
5.2 KiB
TypeScript
177 lines
5.2 KiB
TypeScript
/**
|
||
* Auth API client for login, register, me, refresh.
|
||
* Uses NEXT_PUBLIC_BASE_API_URL on client (defaults to same as BASE_API_URL for server).
|
||
*/
|
||
|
||
const getBaseUrl = () =>
|
||
typeof window !== "undefined"
|
||
? process.env.NEXT_PUBLIC_BASE_API_URL ?? "http://127.0.0.1:8080"
|
||
: process.env.BASE_API_URL ?? process.env.NEXT_PUBLIC_BASE_API_URL ?? "http://127.0.0.1:8080";
|
||
|
||
const API_PREFIX = "/api/v1/auth";
|
||
|
||
export interface AuthUser {
|
||
id: number;
|
||
email: string;
|
||
first_name: string;
|
||
last_name: string;
|
||
username?: string;
|
||
is_admin: boolean;
|
||
email_verified?: boolean;
|
||
}
|
||
|
||
export interface LoginResponse {
|
||
access_token: string;
|
||
refresh_token: string;
|
||
user: AuthUser;
|
||
}
|
||
|
||
export interface RegisterResponse {
|
||
message: string;
|
||
user: AuthUser;
|
||
}
|
||
|
||
export interface RefreshResponse {
|
||
access_token: string;
|
||
refresh_token: string;
|
||
}
|
||
|
||
/** POST /api/v1/auth/login – doğrudan backend (token client’ta; cookie için loginViaCookie kullanın) */
|
||
export async function login(
|
||
email: string,
|
||
password: string
|
||
): Promise<LoginResponse> {
|
||
const base = getBaseUrl();
|
||
const res = await fetch(`${base}${API_PREFIX}/login`, {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json", accept: "application/json" },
|
||
body: JSON.stringify({ email, password }),
|
||
});
|
||
if (!res.ok) {
|
||
const err = await res.json().catch(() => ({}));
|
||
throw new Error((err as { detail?: string }).detail ?? "Giriş başarısız");
|
||
}
|
||
return res.json();
|
||
}
|
||
|
||
/** Cookie tabanlı giriş – token’lar HTTP-only secure cookie’de saklanır */
|
||
export async function loginViaCookie(
|
||
email: string,
|
||
password: string
|
||
): Promise<{ user: AuthUser }> {
|
||
const res = await fetch("/api/auth/cookie-login", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ email: email.trim(), password }),
|
||
credentials: "include",
|
||
});
|
||
const data = await res.json().catch(() => ({}));
|
||
if (!res.ok) {
|
||
throw new Error((data as { error?: string }).error ?? "Giriş başarısız");
|
||
}
|
||
return data as { user: AuthUser };
|
||
}
|
||
|
||
/** Cookie tabanlı çıkış */
|
||
export async function logoutViaCookie(): Promise<void> {
|
||
await fetch("/api/auth/cookie-logout", {
|
||
method: "POST",
|
||
credentials: "include",
|
||
});
|
||
}
|
||
|
||
/** Cookie oturumunu kontrol et (client tarafında oturum bilgisi için) */
|
||
export async function getCookieSession(): Promise<{
|
||
loggedIn: boolean;
|
||
user?: AuthUser;
|
||
}> {
|
||
const res = await fetch("/api/auth/cookie-session", { credentials: "include" });
|
||
const data = await res.json().catch(() => ({ loggedIn: false }));
|
||
return data as { loggedIn: boolean; user?: AuthUser };
|
||
}
|
||
|
||
/** POST /api/v1/auth/register */
|
||
export async function register(body: {
|
||
email: string;
|
||
first_name: string;
|
||
last_name: string;
|
||
password: string;
|
||
username: string;
|
||
}): Promise<RegisterResponse> {
|
||
const base = getBaseUrl();
|
||
const res = await fetch(`${base}${API_PREFIX}/register`, {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json", accept: "application/json" },
|
||
body: JSON.stringify(body),
|
||
});
|
||
if (!res.ok) {
|
||
const err = await res.json().catch(() => ({}));
|
||
const detail = (err as { detail?: string | string[] }).detail;
|
||
const message = Array.isArray(detail) ? detail.join(" ") : detail ?? "Kayıt başarısız";
|
||
throw new Error(message);
|
||
}
|
||
return res.json();
|
||
}
|
||
|
||
/** GET /api/v1/auth/me */
|
||
export async function me(accessToken: string): Promise<{ user: AuthUser }> {
|
||
const base = getBaseUrl();
|
||
const res = await fetch(`${base}${API_PREFIX}/me`, {
|
||
headers: {
|
||
accept: "application/json",
|
||
Authorization: `Bearer ${accessToken}`,
|
||
},
|
||
});
|
||
if (!res.ok) throw new Error("Oturum bilgisi alınamadı");
|
||
return res.json();
|
||
}
|
||
|
||
/** POST /api/v1/auth/refresh */
|
||
export async function refresh(refreshToken: string): Promise<RefreshResponse> {
|
||
const base = getBaseUrl();
|
||
const res = await fetch(`${base}${API_PREFIX}/refresh`, {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json", accept: "application/json" },
|
||
body: JSON.stringify({ refresh_token: refreshToken }),
|
||
});
|
||
if (!res.ok) throw new Error("Token yenilenemedi");
|
||
return res.json();
|
||
}
|
||
|
||
const ACCESS_KEY = "auth_access_token";
|
||
const REFRESH_KEY = "auth_refresh_token";
|
||
|
||
const AUTH_CHANGE_EVENT = "auth-change";
|
||
|
||
/** Header vb. bileşenlerin oturum değişikliğini algılaması için tetiklenir. */
|
||
export function notifyAuthChange(): void {
|
||
if (typeof window === "undefined") return;
|
||
window.dispatchEvent(new Event(AUTH_CHANGE_EVENT));
|
||
}
|
||
|
||
export function setTokens(access: string, refreshToken: string): void {
|
||
if (typeof window === "undefined") return;
|
||
localStorage.setItem(ACCESS_KEY, access);
|
||
localStorage.setItem(REFRESH_KEY, refreshToken);
|
||
notifyAuthChange();
|
||
}
|
||
|
||
export function getAccessToken(): string | null {
|
||
if (typeof window === "undefined") return null;
|
||
return localStorage.getItem(ACCESS_KEY);
|
||
}
|
||
|
||
export function getRefreshToken(): string | null {
|
||
if (typeof window === "undefined") return null;
|
||
return localStorage.getItem(REFRESH_KEY);
|
||
}
|
||
|
||
export function clearTokens(): void {
|
||
if (typeof window === "undefined") return;
|
||
localStorage.removeItem(ACCESS_KEY);
|
||
localStorage.removeItem(REFRESH_KEY);
|
||
notifyAuthChange();
|
||
}
|
||
|
||
export { AUTH_CHANGE_EVENT };
|