Investigate login/signup failure
Investigate and address the "Failed to fetch" error during signup and login failures.
This commit is contained in:
@@ -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'
|
||||
}));
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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';
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user