Investigate login/signup failure

Investigate and address the "Failed to fetch" error during signup and login failures.
This commit is contained in:
gpt-engineer-app[bot]
2025-03-15 15:48:52 +00:00
parent f25d55690d
commit 348194ccec
5 changed files with 97 additions and 30 deletions

View File

@@ -23,8 +23,8 @@ const SupabaseSettingsForm: React.FC<SupabaseSettingsFormProps> = ({ onSaved })
const [formState, setFormState] = useState({
supabaseUrl: '',
supabaseKey: '',
useProxy: false,
proxyType: 'corsproxy.io',
useProxy: true, // 기본값을 true로 변경
proxyType: 'cloudflare', // 기본값을 cloudflare로 변경
isSaving: false
});
@@ -37,15 +37,15 @@ const SupabaseSettingsForm: React.FC<SupabaseSettingsFormProps> = ({ onSaved })
useEffect(() => {
const savedUrl = localStorage.getItem('supabase_url');
const savedKey = localStorage.getItem('supabase_key');
const proxyEnabled = isCorsProxyEnabled();
const proxyEnabled = localStorage.getItem('use_cors_proxy') === 'true';
const savedProxyType = getProxyType();
setFormState(prev => ({
...prev,
supabaseUrl: savedUrl || '',
supabaseKey: savedKey || '',
useProxy: proxyEnabled,
proxyType: savedProxyType
useProxy: proxyEnabled === false ? false : true, // 저장된 값이 명시적으로 false인 경우에만 false, 아니면 기본값 true
proxyType: savedProxyType || 'cloudflare'
}));
}, []);

View File

@@ -1,7 +1,7 @@
import { supabase } from '@/lib/supabase';
import { parseResponse, showAuthToast, handleNetworkError } from '@/utils/auth';
import { getProxyType, isCorsProxyEnabled } from '@/lib/supabase/config';
import { getProxyType, isCorsProxyEnabled, getSupabaseUrl, getOriginalSupabaseUrl } from '@/lib/supabase/config';
/**
* 직접 API 호출을 통한 로그인 시도 (대체 방법)
@@ -11,23 +11,27 @@ export const signInWithDirectApi = async (email: string, password: string) => {
try {
// API 호출 URL 및 헤더 설정
const supabaseUrl = localStorage.getItem('supabase_url') || 'https://a11.ism.kr';
const supabaseUrl = getOriginalSupabaseUrl(); // 원본 URL 사용
const proxyUrl = getSupabaseUrl(); // 프록시 적용된 URL
const supabaseKey = localStorage.getItem('supabase_key') || supabase.supabaseKey;
// 프록시 정보 로그
const usingProxy = isCorsProxyEnabled();
const proxyType = getProxyType();
console.log(`CORS 프록시 사용: ${usingProxy ? '예' : '아니오'}, 타입: ${proxyType}`);
console.log(`CORS 프록시 사용: ${usingProxy ? '예' : '아니오'}, 타입: ${proxyType}, 프록시 URL: ${proxyUrl}`);
// 실제 요청에 사용할 URL 결정 (항상 프록시 URL 사용)
const useUrl = usingProxy ? proxyUrl : supabaseUrl;
// URL에 auth/v1이 이미 포함되어있는지 확인
const baseUrl = supabaseUrl.includes('/auth/v1') ? supabaseUrl : `${supabaseUrl}/auth/v1`;
const baseUrl = useUrl.includes('/auth/v1') ? useUrl : `${useUrl}/auth/v1`;
// 토큰 엔드포인트 경로 (curl 테스트와 동일한 형식으로)
// 토큰 엔드포인트 경로
const tokenUrl = `${baseUrl}/token?grant_type=password`;
console.log('로그인 API 요청 URL:', tokenUrl);
// 로그인 요청 보내기 (curl 테스트와 동일한 형식으로)
// 로그인 요청 보내기
const response = await fetch(tokenUrl, {
method: 'POST',
headers: {

View File

@@ -1,6 +1,7 @@
import { supabase } from '@/lib/supabase';
import { parseResponse, showAuthToast } from '@/utils/auth';
import { getProxyType, isCorsProxyEnabled, getSupabaseUrl, getOriginalSupabaseUrl } from '@/lib/supabase/config';
/**
* 직접 API 호출을 통한 회원가입
@@ -9,11 +10,21 @@ export const signUpWithDirectApi = async (email: string, password: string, usern
try {
console.log('직접 API 호출로 회원가입 시도 중');
const supabaseUrl = localStorage.getItem('supabase_url') || 'https://a11.ism.kr';
// 프록시 적용된 URL과 원본 URL 모두 가져오기
const supabaseUrl = getOriginalSupabaseUrl(); // 원본 URL
const proxyUrl = getSupabaseUrl(); // 프록시 적용된 URL
const supabaseKey = localStorage.getItem('supabase_key') || supabase.supabaseKey;
// 프록시 정보 로깅
const usingProxy = isCorsProxyEnabled();
const proxyType = getProxyType();
console.log(`CORS 프록시 사용: ${usingProxy ? '예' : '아니오'}, 타입: ${proxyType}, 프록시 URL: ${proxyUrl}`);
// 실제 요청에 사용할 URL 결정 (항상 프록시 URL 사용)
const useUrl = usingProxy ? proxyUrl : supabaseUrl;
// URL에 auth/v1이 이미 포함되어있는지 확인
const baseUrl = supabaseUrl.includes('/auth/v1') ? supabaseUrl : `${supabaseUrl}/auth/v1`;
const baseUrl = useUrl.includes('/auth/v1') ? useUrl : `${useUrl}/auth/v1`;
// 회원가입 API 엔드포인트 및 헤더 설정
const signUpUrl = `${baseUrl}/signup`;
@@ -125,7 +136,23 @@ export const signUpWithDirectApi = async (email: string, password: string, usern
} catch (error: any) {
console.error('회원가입 API 호출 중 예외 발생:', error);
const errorMessage = error.message || '알 수 없는 오류가 발생했습니다.';
// 프록시 설정 확인
const usingProxy = isCorsProxyEnabled();
const proxyType = getProxyType();
// 오류 메시지 설정 및 프록시 추천
let errorMessage = error.message || '알 수 없는 오류가 발생했습니다.';
if (errorMessage.includes('Failed to fetch') ||
errorMessage.includes('NetworkError') ||
errorMessage.includes('CORS')) {
if (!usingProxy) {
errorMessage += ' (설정에서 Cloudflare CORS 프록시 활성화를 권장합니다)';
} else if (proxyType !== 'cloudflare') {
errorMessage += ' (설정에서 Cloudflare CORS 프록시로 변경을 권장합니다)';
}
}
showAuthToast('회원가입 오류', errorMessage, 'destructive');
return { error: { message: errorMessage }, user: null };

View File

@@ -54,6 +54,12 @@ export const getSupabaseUrl = () => {
return 'https://a11.ism.kr';
};
// 원본 URL 반환 (프록시 없는 URL)
export const getOriginalSupabaseUrl = () => {
const storedUrl = localStorage.getItem('supabase_url');
return storedUrl || 'https://a11.ism.kr';
};
export const getSupabaseKey = () => {
// 로컬 스토리지에서 설정된 키를 우선 사용
const storedKey = localStorage.getItem('supabase_key');
@@ -84,7 +90,7 @@ export const isCorsProxyEnabled = () => {
};
// 온프레미스 연결을 위한 설정 도우미 함수
export const configureSupabase = (url: string, key: string, useProxy: boolean = false, proxyType: string = 'corsproxy.io') => {
export const configureSupabase = (url: string, key: string, useProxy: boolean = true, proxyType: string = 'cloudflare') => {
// URL 정리 (앞뒤 공백 제거)
const cleanUrl = url.trim();
@@ -108,9 +114,3 @@ export const configureSupabase = (url: string, key: string, useProxy: boolean =
// 페이지 새로고침 - 새로운 설정으로 Supabase 클라이언트 초기화
window.location.reload();
};
// 원본 URL 반환 (프록시 없는 URL)
export const getOriginalSupabaseUrl = () => {
const storedUrl = localStorage.getItem('supabase_url');
return storedUrl || 'https://a11.ism.kr';
};

View File

@@ -1,6 +1,6 @@
import { supabase } from '@/lib/supabase';
import { getProxyType, isCorsProxyEnabled } from '@/lib/supabase/config';
import { getProxyType, isCorsProxyEnabled, getSupabaseUrl, getOriginalSupabaseUrl } from '@/lib/supabase/config';
/**
* CORS 문제 확인
@@ -28,8 +28,8 @@ export const verifyServerConnection = async (): Promise<{
try {
const start = Date.now();
// Supabase URL 가져오기
const supabaseUrl = supabase.auth.url;
// Supabase URL 가져오기 (프록시 적용 URL)
const supabaseUrl = getSupabaseUrl();
if (!supabaseUrl) {
return {
@@ -41,7 +41,7 @@ export const verifyServerConnection = async (): Promise<{
// 프록시 설정 상태 확인
const usingProxy = isCorsProxyEnabled();
const proxyType = getProxyType();
console.log(`연결 테스트 - CORS 프록시: ${usingProxy ? '사용 중' : '미사용'}, 타입: ${proxyType}`);
console.log(`연결 테스트 - CORS 프록시: ${usingProxy ? '사용 중' : '미사용'}, 타입: ${proxyType}, URL: ${supabaseUrl}`);
// 단순 헬스 체크 요청
try {
@@ -51,7 +51,7 @@ export const verifyServerConnection = async (): Promise<{
'Content-Type': 'application/json',
'apikey': localStorage.getItem('supabase_key') || ''
},
signal: AbortSignal.timeout(5000) // 타임아웃 시간 증가
signal: AbortSignal.timeout(8000) // 타임아웃 시간 증가
});
const elapsed = Date.now() - start;
@@ -73,6 +73,15 @@ export const verifyServerConnection = async (): Promise<{
} catch (fetchError: any) {
console.error('기본 연결 확인 실패, 상태 확인 시도:', fetchError);
// HTTP URL을 사용하는데 프록시가 비활성화된 경우
const originalUrl = getOriginalSupabaseUrl();
if (originalUrl.startsWith('http:') && !usingProxy) {
return {
connected: false,
message: 'HTTP URL에 직접 접근할 수 없습니다. CORS 프록시를 활성화하세요.'
};
}
try {
// 대체 경로로 상태 확인
const altResponse = await fetch(`${supabaseUrl}/`, {
@@ -80,7 +89,7 @@ export const verifyServerConnection = async (): Promise<{
headers: {
'Content-Type': 'application/json'
},
signal: AbortSignal.timeout(5000)
signal: AbortSignal.timeout(8000)
});
// 어떤 응답이라도 오면 서버가 살아있다고 간주
@@ -121,12 +130,20 @@ export const verifyServerConnection = async (): Promise<{
}
}
// HTTP URL을 사용하는데 프록시가 비활성화된 경우
const originalUrl = getOriginalSupabaseUrl();
if (originalUrl.startsWith('http:') && !usingProxy) {
errorMessage = 'HTTP URL에 직접 접근할 수 없습니다. CORS 프록시를 활성화하세요.';
}
// Cloudflare 프록시 추천 메시지 추가
if (errorMessage.includes('CORS') || errorMessage.includes('fetch') || errorMessage.includes('네트워크')) {
if (!usingProxy) {
console.log('CORS 오류 감지, Cloudflare 프록시 사용 권장');
errorMessage += '. Cloudflare CORS 프록시 사용을 권장합니다.';
} else if (proxyType !== 'cloudflare') {
console.log('CORS 오류 감지, Cloudflare 프록시로 변경 권장');
errorMessage += '. Cloudflare CORS 프록시로 변경을 권장합니다.';
}
}
@@ -146,7 +163,7 @@ export const verifySupabaseConnection = async (): Promise<{
statusCode?: number;
details?: string;
}> => {
const supabaseUrl = localStorage.getItem('supabase_url');
const supabaseUrl = getSupabaseUrl();
if (!supabaseUrl) {
return {
connected: false,
@@ -154,6 +171,11 @@ export const verifySupabaseConnection = async (): Promise<{
};
}
// 프록시 정보 로깅
const usingProxy = isCorsProxyEnabled();
const proxyType = getProxyType();
console.log(`강화된 연결 테스트 - 프록시: ${usingProxy ? '사용 중' : '미사용'}, 타입: ${proxyType}, URL: ${supabaseUrl}`);
// 무작위 쿼리 파라미터를 추가하여 캐시 방지
const cacheParam = `?_nocache=${Date.now()}`;
@@ -167,14 +189,18 @@ export const verifySupabaseConnection = async (): Promise<{
for (const path of paths) {
try {
console.log(`경로 시도: ${path}`);
const response = await fetch(`${supabaseUrl}${path}${cacheParam}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
'Content-Type': 'application/json',
'apikey': localStorage.getItem('supabase_key') || ''
},
signal: AbortSignal.timeout(5000)
signal: AbortSignal.timeout(8000)
});
console.log(`경로 ${path} 응답 상태:`, response.status);
// 어떤 응답이든 서버가 살아있다는 신호로 간주
return {
connected: true,
@@ -188,6 +214,16 @@ export const verifySupabaseConnection = async (): Promise<{
}
}
// HTTP URL을 사용하는데 프록시가 비활성화된 경우
const originalUrl = getOriginalSupabaseUrl();
if (originalUrl.startsWith('http:') && !usingProxy) {
return {
connected: false,
message: 'HTTP URL에 직접 접근할 수 없습니다. CORS 프록시를 활성화하세요.',
details: 'CORS 제한으로 인해 HTTP URL에 직접 접근할 수 없습니다'
};
}
// 모든 경로 시도 실패
return {
connected: false,