You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

62 lines
2.4 KiB
TypeScript

// frontend/lib/auth.ts — phase-15 인증 클라이언트 헬퍼.
// 데모 무손상: 이 모듈은 호출되기 전엔 부작용이 없다. 게이트는 proxy.ts(미들웨어)가 담당.
import type { Me } from "@/lib/types";
const BASE = process.env.NEXT_PUBLIC_API_BASE ?? (typeof window === "undefined" ? "http://localhost:31800" : "");
const AUTH_ENABLED = process.env.NEXT_PUBLIC_AUTH_ENABLED === "true";
/** 로그인. 성공 시 MeOut, 실패(401)면 에러를 던진다(쿠키는 백엔드 Set-Cookie). */
export async function login(email: string, password: string): Promise<Me> {
const res = await fetch(`${BASE}/api/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify({ email, password }),
});
if (!res.ok) {
throw new Error("이메일 또는 비밀번호가 올바르지 않아요");
}
return res.json() as Promise<Me>;
}
/** 로그아웃. 쿠키를 비운다. 실패해도 조용히 무시(이미 로그아웃 상태일 수 있음). */
export async function logout(): Promise<void> {
try {
await fetch(`${BASE}/api/auth/logout`, {
method: "POST",
credentials: "include",
});
} catch {
// 네트워크 오류는 무시 — UI는 로그인 화면으로 전이하면 충분.
}
}
/** 현재 사용자. 세션 없으면(401) null. */
export async function getMe(signal?: AbortSignal): Promise<Me | null> {
const res = await fetch(`${BASE}/api/me`, {
credentials: "include",
cache: "no-store",
signal,
});
if (res.status === 401) return null;
if (!res.ok) return null;
return res.json() as Promise<Me>;
}
/**
* 인증 켜짐일 때만 동작하는 얇은 fetch 래퍼. 401이면 /login?next=<현재경로>로 보낸다.
* 데모(AUTH_ENABLED=false)에선 일반 fetch와 동일 — 부작용 없음.
* 중앙 래퍼가 없으므로 전 호출을 개조하지 않는다(라우트 보호는 proxy.ts가 담당, 이건 보조).
*/
export async function apiFetch(input: string, init?: RequestInit): Promise<Response> {
const url = input.startsWith("http")
? input
: `${BASE}${input.startsWith("/") ? "" : "/"}${input}`;
const res = await fetch(url, { credentials: "include", ...init });
if (AUTH_ENABLED && res.status === 401 && typeof window !== "undefined") {
const next = window.location.pathname + window.location.search;
window.location.assign(`/login?next=${encodeURIComponent(next)}`);
}
return res;
}