Update Supabase connection tests

Improve error handling for REST API and database connection tests.
This commit is contained in:
gpt-engineer-app[bot]
2025-03-15 12:55:36 +00:00
parent b30fb33384
commit 5fd4c183eb
5 changed files with 82 additions and 18 deletions

View File

@@ -8,24 +8,25 @@ interface ProxyRecommendationAlertProps {
} }
const ProxyRecommendationAlert: React.FC<ProxyRecommendationAlertProps> = ({ errors }) => { const ProxyRecommendationAlert: React.FC<ProxyRecommendationAlertProps> = ({ errors }) => {
const hasProxyRecommendation = errors.some(err => const hasCorsError = errors.some(err =>
err.includes('프록시 사용 시 정상 작동합니다') || err.includes('CORS') ||
err.includes('프록시를 선택해보세요') err.includes('Failed to fetch') ||
err.includes('프록시 사용시 정상 작동') ||
err.includes('프록시를 활성화')
); );
if (!hasProxyRecommendation || errors.length === 0) return null; if (!hasCorsError || errors.length === 0) return null;
const recommendationMessage = errors.find(err =>
err.includes('프록시 사용 시 정상 작동합니다') ||
err.includes('프록시를 선택해보세요')
);
return ( return (
<Alert className="bg-amber-50 border-amber-200 mt-3"> <Alert className="bg-amber-50 border-amber-200 mt-3">
<AlertCircle className="h-4 w-4 text-amber-600" /> <AlertCircle className="h-4 w-4 text-amber-600" />
<AlertTitle className="text-amber-800 text-xs font-medium"> </AlertTitle> <AlertTitle className="text-amber-800 text-xs font-medium">CORS </AlertTitle>
<AlertDescription className="text-amber-700 text-xs"> <AlertDescription className="text-amber-700 text-xs">
{recommendationMessage} <p>HTTP URL에 .</p>
<ul className="list-disc pl-4 mt-1">
<li className="mt-1">CORS .</li>
<li className="mt-1"> HTTPS URL로 .</li>
</ul>
</AlertDescription> </AlertDescription>
</Alert> </Alert>
); );

View File

@@ -24,6 +24,7 @@ const ProxyTypeSelector: React.FC<ProxyTypeSelectorProps> = ({
<SelectItem value="thingproxy">thingproxy.freeboard.io</SelectItem> <SelectItem value="thingproxy">thingproxy.freeboard.io</SelectItem>
<SelectItem value="allorigins">allorigins.win</SelectItem> <SelectItem value="allorigins">allorigins.win</SelectItem>
<SelectItem value="cors-anywhere">cors-anywhere.herokuapp.com</SelectItem> <SelectItem value="cors-anywhere">cors-anywhere.herokuapp.com</SelectItem>
<SelectItem value="cloudflare">Cloudflare Workers </SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
<p className="text-xs text-gray-500 mt-1"> <p className="text-xs text-gray-500 mt-1">

View File

@@ -31,6 +31,14 @@ export const getSupabaseUrl = () => {
case 'cors-anywhere': case 'cors-anywhere':
proxyUrl = `https://cors-anywhere.herokuapp.com/${urlForProxy}`; proxyUrl = `https://cors-anywhere.herokuapp.com/${urlForProxy}`;
break; break;
case 'cloudflare':
// Cloudflare Workers CORS 프록시
proxyUrl = `https://cors-proxy.azurewebsites.net/api/cors-proxy?url=${encodeURIComponent(urlForProxy)}`;
break;
case 'local-proxy':
// 사용자 지정 로컬 프록시 (개발 환경용)
proxyUrl = `http://localhost:8080/proxy?url=${encodeURIComponent(urlForProxy)}`;
break;
default: default:
proxyUrl = `https://corsproxy.io/?${encodeURIComponent(urlForProxy)}`; proxyUrl = `https://corsproxy.io/?${encodeURIComponent(urlForProxy)}`;
} }
@@ -42,8 +50,7 @@ export const getSupabaseUrl = () => {
} }
// 기본값 사용 (환경 변수 대신) // 기본값 사용 (환경 변수 대신)
const defaultUrl = 'http://a11.ism.kr:8000'; return 'http://a11.ism.kr:8000';
return defaultUrl;
}; };
export const getSupabaseKey = () => { export const getSupabaseKey = () => {
@@ -85,6 +92,12 @@ export const configureSupabase = (url: string, key: string, useProxy: boolean =
? cleanUrl ? cleanUrl
: `http://${cleanUrl}`; : `http://${cleanUrl}`;
// HTTP URL을 사용하고 프록시가 활성화되지 않은 경우 경고
const isHttpUrl = normalizedUrl.startsWith('http:') && !normalizedUrl.startsWith('http://localhost');
if (isHttpUrl && !useProxy) {
console.warn('경고: HTTP URL을 사용하면서 CORS 프록시가 비활성화되어 있습니다. 브라우저에서 접근 문제가 발생할 수 있습니다.');
}
// 로컬 스토리지에 설정 저장 // 로컬 스토리지에 설정 저장
localStorage.setItem('supabase_url', normalizedUrl); localStorage.setItem('supabase_url', normalizedUrl);
localStorage.setItem('supabase_key', key); localStorage.setItem('supabase_key', key);

View File

@@ -12,7 +12,6 @@ export const testRestApi = async (
try { try {
const originalUrl = getSupabaseUrl(); const originalUrl = getSupabaseUrl();
const supabaseKey = getSupabaseKey(); const supabaseKey = getSupabaseKey();
const proxyType = 'corsproxy.io'; // 기본값
if (!originalUrl || !supabaseKey) { if (!originalUrl || !supabaseKey) {
return { return {
@@ -21,13 +20,32 @@ export const testRestApi = async (
}; };
} }
// HTTP URL 감지
const isHttpUrl = originalUrl.startsWith('http:') && !originalUrl.startsWith('http://localhost');
// 클라이언트 인스턴스로 간단한 API 호출 수행 // 클라이언트 인스턴스로 간단한 API 호출 수행
const { data, error } = await supabase const { data, error } = await supabase
.from('_tests') .from('_tests')
.select('*') .select('*')
.limit(1); .limit(1);
if (error && error.code !== '42P01' && error.code !== 'PGRST116') { if (error) {
// CORS 관련 오류인지 확인
if (error.message && error.message.includes('fetch failed') && isHttpUrl) {
return {
success: false,
error: `REST API 요청 실패: ${error.message} (CORS 오류 가능성 높음. CORS 프록시 사용을 권장합니다)`
};
}
// 테이블이 없는 경우 정상 (테스트 테이블이 없을 수 있음)
if (error.code === '42P01' || error.code === 'PGRST116') {
return {
success: true,
error: null
};
}
return { return {
success: false, success: false,
error: `REST API 요청 실패: ${error.message}` error: `REST API 요청 실패: ${error.message}`
@@ -39,6 +57,17 @@ export const testRestApi = async (
error: null error: null
}; };
} catch (err: any) { } catch (err: any) {
// HTTP URL을 사용하고 있는지 확인
const url = getSupabaseUrl();
const isHttpUrl = url.startsWith('http:') && !url.startsWith('http://localhost');
if (err.message && err.message.includes('Failed to fetch') && isHttpUrl) {
return {
success: false,
error: `REST API 테스트 실패: ${err.message} (CORS 프록시 사용시 정상 작동합니다. 설정에서 프록시를 활성화해보세요)`
};
}
return { return {
success: false, success: false,
error: `REST API 테스트 예외: ${err.message || '알 수 없는 오류'}` error: `REST API 테스트 예외: ${err.message || '알 수 없는 오류'}`

View File

@@ -1,6 +1,7 @@
import { SupabaseClient } from '@supabase/supabase-js'; import { SupabaseClient } from '@supabase/supabase-js';
import { TestResult } from './types'; import { TestResult } from './types';
import { getSupabaseUrl } from '../config';
export const testDatabaseConnection = async ( export const testDatabaseConnection = async (
supabase: SupabaseClient supabase: SupabaseClient
@@ -13,8 +14,16 @@ export const testDatabaseConnection = async (
.limit(1) .limit(1)
.maybeSingle(); .maybeSingle();
// 오류가 있으면 실패로 처리 (404 제외 - 테이블이 없는 것은 정상) // 테이블이 없는 경우 정상 (테스트 테이블이 없을 수 있음)
if (error && error.code !== '42P01' && error.code !== 'PGRST116') { if (error && (error.code === '42P01' || error.code === 'PGRST116')) {
return {
success: true,
error: null
};
}
// 다른 오류가 있으면 실패로 처리
if (error) {
return { return {
success: false, success: false,
error: `데이터베이스 연결 오류: ${error.message}` error: `데이터베이스 연결 오류: ${error.message}`
@@ -27,6 +36,17 @@ export const testDatabaseConnection = async (
error: null error: null
}; };
} catch (error: any) { } catch (error: any) {
// HTTP URL 관련 CORS 오류 확인
const url = getSupabaseUrl();
const isHttpUrl = url.startsWith('http:') && !url.startsWith('http://localhost');
if (error.message && error.message.includes('Failed to fetch') && isHttpUrl) {
return {
success: false,
error: `데이터베이스 테스트 실패: ${error.message} (CORS 프록시 사용시 정상 작동합니다. 설정에서 프록시를 활성화해보세요)`
};
}
return { return {
success: false, success: false,
error: `데이터베이스 테스트 중 예외 발생: ${error.message || '알 수 없는 오류'}` error: `데이터베이스 테스트 중 예외 발생: ${error.message || '알 수 없는 오류'}`