검증게시판 단계 추가
This commit is contained in:
@@ -26,6 +26,11 @@ export function setApiBase(url: string): void {
|
||||
storageSetSync('gc_api_base_url', API_BASE);
|
||||
}
|
||||
|
||||
/** initStorage() 이후 Preferences에 저장된 API 주소를 반영 */
|
||||
export function refreshApiBaseFromStorage(): void {
|
||||
API_BASE = resolveApiBase();
|
||||
}
|
||||
|
||||
/** STOMP WebSocket 연결에 사용할 HTTP base URL (api 경로 제거) */
|
||||
export function getApiBase(): string {
|
||||
return API_BASE.replace(/\/api$/, '');
|
||||
@@ -77,12 +82,56 @@ function authHeaders(): Record<string, string> {
|
||||
return headers;
|
||||
}
|
||||
|
||||
const DEFAULT_FETCH_TIMEOUT_MS = 30_000;
|
||||
const LOGIN_FETCH_TIMEOUT_MS = 60_000;
|
||||
|
||||
function wrapNetworkError(e: unknown): Error {
|
||||
if (e instanceof TypeError || (e instanceof Error && /failed to fetch/i.test(e.message))) {
|
||||
return new Error(
|
||||
`서버에 연결할 수 없습니다 (${API_BASE}). Wi‑Fi·API 주소·앱 재설치 후 설정의 API URL을 확인하세요.`,
|
||||
);
|
||||
}
|
||||
return e instanceof Error ? e : new Error(String(e));
|
||||
}
|
||||
|
||||
async function fetchWithTimeout(
|
||||
url: string,
|
||||
init: RequestInit | undefined,
|
||||
timeoutMs: number,
|
||||
): Promise<Response> {
|
||||
const controller = new AbortController();
|
||||
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
||||
try {
|
||||
return await fetch(url, {
|
||||
...init,
|
||||
signal: controller.signal,
|
||||
headers: { ...authHeaders(), ...(init?.headers ?? {}) },
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof DOMException && e.name === 'AbortError') {
|
||||
throw new Error(
|
||||
`서버 응답 시간 초과 (${Math.round(timeoutMs / 1000)}초). ${API_BASE} · Wi‑Fi를 확인하세요.`,
|
||||
);
|
||||
}
|
||||
throw wrapNetworkError(e);
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
|
||||
/** 로그인 API 오류 시 메시지 throw */
|
||||
async function requestOrThrow<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
const res = await fetch(`${API_BASE}${path}`, {
|
||||
...init,
|
||||
headers: { ...authHeaders(), ...(init?.headers ?? {}) },
|
||||
});
|
||||
async function requestOrThrow<T>(
|
||||
path: string,
|
||||
init?: RequestInit,
|
||||
timeoutMs: number = DEFAULT_FETCH_TIMEOUT_MS,
|
||||
): Promise<T> {
|
||||
let res: Response;
|
||||
try {
|
||||
res = await fetchWithTimeout(`${API_BASE}${path}`, init, timeoutMs);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) throw e;
|
||||
throw wrapNetworkError(e);
|
||||
}
|
||||
const text = await res.text().catch(() => '');
|
||||
if (!res.ok) {
|
||||
let msg = `요청 실패 (${res.status})`;
|
||||
@@ -99,10 +148,7 @@ async function requestOrThrow<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
|
||||
async function request<T>(path: string, init?: RequestInit): Promise<T | null> {
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}${path}`, {
|
||||
...init,
|
||||
headers: { ...authHeaders(), ...(init?.headers ?? {}) },
|
||||
});
|
||||
const res = await fetchWithTimeout(`${API_BASE}${path}`, init, DEFAULT_FETCH_TIMEOUT_MS);
|
||||
if (res.status === 204) return null;
|
||||
if (!res.ok) {
|
||||
console.error(`[backendApi] ${init?.method ?? 'GET'} ${path} → ${res.status}`);
|
||||
@@ -814,10 +860,14 @@ export async function saveRolePermissions(
|
||||
}
|
||||
|
||||
export async function loginUser(username: string, password: string): Promise<LoginResponse> {
|
||||
return requestOrThrow<LoginResponse>('/auth/login', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ username, password }),
|
||||
});
|
||||
return requestOrThrow<LoginResponse>(
|
||||
'/auth/login',
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ username, password }),
|
||||
},
|
||||
LOGIN_FETCH_TIMEOUT_MS,
|
||||
);
|
||||
}
|
||||
|
||||
export async function fetchAuthMe(): Promise<LoginResponse | null> {
|
||||
|
||||
Reference in New Issue
Block a user