Refactor SupabaseSettingsForm component
The SupabaseSettingsForm component was refactored into smaller, more manageable components to improve readability and maintainability.
This commit is contained in:
39
src/components/supabase/CorsProxyToggle.tsx
Normal file
39
src/components/supabase/CorsProxyToggle.tsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { Switch } from "@/components/ui/switch";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Shield } from "lucide-react";
|
||||||
|
|
||||||
|
interface CorsProxyToggleProps {
|
||||||
|
useProxy: boolean;
|
||||||
|
setUseProxy: (use: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CorsProxyToggle: React.FC<CorsProxyToggleProps> = ({
|
||||||
|
useProxy,
|
||||||
|
setUseProxy
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-between space-x-2 py-2">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<Label htmlFor="cors-proxy" className="cursor-pointer">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Shield className="h-4 w-4 mr-2 text-neuro-income" />
|
||||||
|
<span>CORS 프록시 사용</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-gray-500">
|
||||||
|
HTTP URL에 접근 문제가 있는 경우 활성화하세요
|
||||||
|
</p>
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
<Switch
|
||||||
|
id="cors-proxy"
|
||||||
|
checked={useProxy}
|
||||||
|
onCheckedChange={setUseProxy}
|
||||||
|
className="data-[state=checked]:bg-neuro-income"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CorsProxyToggle;
|
||||||
36
src/components/supabase/ProxyTypeSelector.tsx
Normal file
36
src/components/supabase/ProxyTypeSelector.tsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
|
|
||||||
|
interface ProxyTypeSelectorProps {
|
||||||
|
proxyType: string;
|
||||||
|
setProxyType: (type: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProxyTypeSelector: React.FC<ProxyTypeSelectorProps> = ({
|
||||||
|
proxyType,
|
||||||
|
setProxyType
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="proxy-type" className="text-base">프록시 서비스 선택</Label>
|
||||||
|
<Select value={proxyType} onValueChange={setProxyType}>
|
||||||
|
<SelectTrigger id="proxy-type" className="w-full">
|
||||||
|
<SelectValue placeholder="프록시 서비스 선택" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="corsproxy.io">corsproxy.io (기본)</SelectItem>
|
||||||
|
<SelectItem value="thingproxy">thingproxy.freeboard.io</SelectItem>
|
||||||
|
<SelectItem value="allorigins">allorigins.win</SelectItem>
|
||||||
|
<SelectItem value="cors-anywhere">cors-anywhere.herokuapp.com</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">
|
||||||
|
특정 프록시 서비스가 작동하지 않으면 다른 서비스를 시도해보세요.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProxyTypeSelector;
|
||||||
36
src/components/supabase/SaveSettingsButton.tsx
Normal file
36
src/components/supabase/SaveSettingsButton.tsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Save, RefreshCw } from "lucide-react";
|
||||||
|
|
||||||
|
interface SaveSettingsButtonProps {
|
||||||
|
onClick: () => void;
|
||||||
|
isSaving: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SaveSettingsButton: React.FC<SaveSettingsButtonProps> = ({
|
||||||
|
onClick,
|
||||||
|
isSaving
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
onClick={onClick}
|
||||||
|
disabled={isSaving}
|
||||||
|
className="w-full bg-neuro-income text-white"
|
||||||
|
>
|
||||||
|
{isSaving ? (
|
||||||
|
<>
|
||||||
|
<RefreshCw className="mr-2 h-4 w-4 animate-spin" />
|
||||||
|
저장 중...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Save className="mr-2 h-4 w-4" />
|
||||||
|
설정 저장
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SaveSettingsButton;
|
||||||
40
src/components/supabase/SupabaseKeyInput.tsx
Normal file
40
src/components/supabase/SupabaseKeyInput.tsx
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
|
||||||
|
interface SupabaseKeyInputProps {
|
||||||
|
supabaseKey: string;
|
||||||
|
setSupabaseKey: (key: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SupabaseKeyInput: React.FC<SupabaseKeyInputProps> = ({
|
||||||
|
supabaseKey,
|
||||||
|
setSupabaseKey
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="supabase-key" className="text-base">Supabase Anon Key</Label>
|
||||||
|
<div className="relative">
|
||||||
|
<svg
|
||||||
|
className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500 h-5 w-5"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" />
|
||||||
|
</svg>
|
||||||
|
<Input
|
||||||
|
id="supabase-key"
|
||||||
|
placeholder="your-anon-key"
|
||||||
|
value={supabaseKey}
|
||||||
|
onChange={(e) => setSupabaseKey(e.target.value)}
|
||||||
|
className="pl-10 neuro-pressed"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SupabaseKeyInput;
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Label } from "@/components/ui/label";
|
|
||||||
import { DatabaseIcon, Save, RefreshCw, Shield, AlertTriangle, Check } from "lucide-react";
|
|
||||||
import { toast } from "@/hooks/useToast.wrapper";
|
import { toast } from "@/hooks/useToast.wrapper";
|
||||||
import {
|
import {
|
||||||
configureSupabase,
|
configureSupabase,
|
||||||
isCorsProxyEnabled,
|
isCorsProxyEnabled,
|
||||||
getProxyType
|
getProxyType
|
||||||
} from "@/lib/supabase/config";
|
} from "@/lib/supabase/config";
|
||||||
import { Switch } from "@/components/ui/switch";
|
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
// 분리된 컴포넌트들 임포트
|
||||||
|
import SupabaseUrlInput from './SupabaseUrlInput';
|
||||||
|
import SupabaseKeyInput from './SupabaseKeyInput';
|
||||||
|
import CorsProxyToggle from './CorsProxyToggle';
|
||||||
|
import ProxyTypeSelector from './ProxyTypeSelector';
|
||||||
|
import SaveSettingsButton from './SaveSettingsButton';
|
||||||
|
|
||||||
interface SupabaseSettingsFormProps {
|
interface SupabaseSettingsFormProps {
|
||||||
onSaved: () => void;
|
onSaved: () => void;
|
||||||
@@ -84,121 +85,35 @@ const SupabaseSettingsForm: React.FC<SupabaseSettingsFormProps> = ({ onSaved })
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const isHttpsUrl = supabaseUrl.startsWith("https://");
|
|
||||||
const suggestProxy = supabaseUrl.startsWith("http://") && !useProxy;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="space-y-2">
|
<SupabaseUrlInput
|
||||||
<Label htmlFor="supabase-url" className="text-base">Supabase URL</Label>
|
supabaseUrl={supabaseUrl}
|
||||||
<div className="relative">
|
setSupabaseUrl={setSupabaseUrl}
|
||||||
<DatabaseIcon className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500 h-5 w-5" />
|
useProxy={useProxy}
|
||||||
<Input
|
|
||||||
id="supabase-url"
|
|
||||||
placeholder="http://your-supabase-url.com"
|
|
||||||
value={supabaseUrl}
|
|
||||||
onChange={(e) => setSupabaseUrl(e.target.value)}
|
|
||||||
className="pl-10 neuro-pressed"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* URL 관련 경고 및 안내 추가 */}
|
<SupabaseKeyInput
|
||||||
{suggestProxy && (
|
supabaseKey={supabaseKey}
|
||||||
<div className="flex items-center text-amber-500 text-xs mt-1 p-2 bg-amber-50 rounded-md">
|
setSupabaseKey={setSupabaseKey}
|
||||||
<AlertTriangle className="h-4 w-4 mr-1 flex-shrink-0" />
|
|
||||||
<span>HTTP URL을 사용하고 계십니다. CORS 프록시 활성화를 권장합니다.</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isHttpsUrl && (
|
|
||||||
<div className="flex items-center text-green-500 text-xs mt-1 p-2 bg-green-50 rounded-md">
|
|
||||||
<Check className="h-4 w-4 mr-1 flex-shrink-0" />
|
|
||||||
<span>HTTPS URL을 사용하고 있습니다. 권장됩니다.</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="supabase-key" className="text-base">Supabase Anon Key</Label>
|
|
||||||
<div className="relative">
|
|
||||||
<svg
|
|
||||||
className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500 h-5 w-5"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" />
|
|
||||||
</svg>
|
|
||||||
<Input
|
|
||||||
id="supabase-key"
|
|
||||||
placeholder="your-anon-key"
|
|
||||||
value={supabaseKey}
|
|
||||||
onChange={(e) => setSupabaseKey(e.target.value)}
|
|
||||||
className="pl-10 neuro-pressed"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* CORS 프록시 설정 추가 */}
|
<CorsProxyToggle
|
||||||
<div className="flex items-center justify-between space-x-2 py-2">
|
useProxy={useProxy}
|
||||||
<div className="space-y-0.5">
|
setUseProxy={setUseProxy}
|
||||||
<Label htmlFor="cors-proxy" className="cursor-pointer">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Shield className="h-4 w-4 mr-2 text-neuro-income" />
|
|
||||||
<span>CORS 프록시 사용</span>
|
|
||||||
</div>
|
|
||||||
<p className="text-xs text-gray-500">
|
|
||||||
HTTP URL에 접근 문제가 있는 경우 활성화하세요
|
|
||||||
</p>
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
<Switch
|
|
||||||
id="cors-proxy"
|
|
||||||
checked={useProxy}
|
|
||||||
onCheckedChange={setUseProxy}
|
|
||||||
className="data-[state=checked]:bg-neuro-income"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 프록시 서비스 선택 옵션 추가 */}
|
|
||||||
{useProxy && (
|
{useProxy && (
|
||||||
<div className="space-y-2">
|
<ProxyTypeSelector
|
||||||
<Label htmlFor="proxy-type" className="text-base">프록시 서비스 선택</Label>
|
proxyType={proxyType}
|
||||||
<Select value={proxyType} onValueChange={setProxyType}>
|
setProxyType={setProxyType}
|
||||||
<SelectTrigger id="proxy-type" className="w-full">
|
/>
|
||||||
<SelectValue placeholder="프록시 서비스 선택" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="corsproxy.io">corsproxy.io (기본)</SelectItem>
|
|
||||||
<SelectItem value="thingproxy">thingproxy.freeboard.io</SelectItem>
|
|
||||||
<SelectItem value="allorigins">allorigins.win</SelectItem>
|
|
||||||
<SelectItem value="cors-anywhere">cors-anywhere.herokuapp.com</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
<p className="text-xs text-gray-500 mt-1">
|
|
||||||
특정 프록시 서비스가 작동하지 않으면 다른 서비스를 시도해보세요.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Button
|
<SaveSettingsButton
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
disabled={isSaving}
|
isSaving={isSaving}
|
||||||
className="w-full bg-neuro-income text-white"
|
/>
|
||||||
>
|
|
||||||
{isSaving ? (
|
|
||||||
<>
|
|
||||||
<RefreshCw className="mr-2 h-4 w-4 animate-spin" />
|
|
||||||
저장 중...
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Save className="mr-2 h-4 w-4" />
|
|
||||||
설정 저장
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
53
src/components/supabase/SupabaseUrlInput.tsx
Normal file
53
src/components/supabase/SupabaseUrlInput.tsx
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { DatabaseIcon, AlertTriangle, Check } from "lucide-react";
|
||||||
|
|
||||||
|
interface SupabaseUrlInputProps {
|
||||||
|
supabaseUrl: string;
|
||||||
|
setSupabaseUrl: (url: string) => void;
|
||||||
|
useProxy: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SupabaseUrlInput: React.FC<SupabaseUrlInputProps> = ({
|
||||||
|
supabaseUrl,
|
||||||
|
setSupabaseUrl,
|
||||||
|
useProxy
|
||||||
|
}) => {
|
||||||
|
const isHttpsUrl = supabaseUrl.startsWith("https://");
|
||||||
|
const suggestProxy = supabaseUrl.startsWith("http://") && !useProxy;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="supabase-url" className="text-base">Supabase URL</Label>
|
||||||
|
<div className="relative">
|
||||||
|
<DatabaseIcon className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500 h-5 w-5" />
|
||||||
|
<Input
|
||||||
|
id="supabase-url"
|
||||||
|
placeholder="http://your-supabase-url.com"
|
||||||
|
value={supabaseUrl}
|
||||||
|
onChange={(e) => setSupabaseUrl(e.target.value)}
|
||||||
|
className="pl-10 neuro-pressed"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* URL 관련 경고 및 안내 추가 */}
|
||||||
|
{suggestProxy && (
|
||||||
|
<div className="flex items-center text-amber-500 text-xs mt-1 p-2 bg-amber-50 rounded-md">
|
||||||
|
<AlertTriangle className="h-4 w-4 mr-1 flex-shrink-0" />
|
||||||
|
<span>HTTP URL을 사용하고 계십니다. CORS 프록시 활성화를 권장합니다.</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isHttpsUrl && (
|
||||||
|
<div className="flex items-center text-green-500 text-xs mt-1 p-2 bg-green-50 rounded-md">
|
||||||
|
<Check className="h-4 w-4 mr-1 flex-shrink-0" />
|
||||||
|
<span>HTTPS URL을 사용하고 있습니다. 권장됩니다.</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SupabaseUrlInput;
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
// 온프레미스 Supabase URL과 anon key 설정
|
// 온프레미스 Supabase URL과 anon key 설정
|
||||||
export const getSupabaseUrl = () => {
|
export const getSupabaseUrl = () => {
|
||||||
// 로컬 스토리지에서 설정된 URL을 우선 사용
|
// 로컬 스토리지에서 설정된 URL을 우선 사용
|
||||||
@@ -42,8 +41,8 @@ export const getSupabaseUrl = () => {
|
|||||||
return storedUrl;
|
return storedUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 환경 변수 또는 기본값 사용
|
// 기본값 사용 (환경 변수 대신)
|
||||||
const defaultUrl = process.env.SUPABASE_URL || 'http://a11.ism.kr:8000';
|
const defaultUrl = 'http://a11.ism.kr:8000';
|
||||||
return defaultUrl;
|
return defaultUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -52,8 +51,8 @@ export const getSupabaseKey = () => {
|
|||||||
const storedKey = localStorage.getItem('supabase_key');
|
const storedKey = localStorage.getItem('supabase_key');
|
||||||
if (storedKey) return storedKey;
|
if (storedKey) return storedKey;
|
||||||
|
|
||||||
// 환경 변수 또는 기본값 사용
|
// 기본값 사용 (환경 변수 대신)
|
||||||
return process.env.SUPABASE_ANON_KEY || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzQyMDM5MzU4LCJleHAiOjQ4OTU2MzkzNTh9.XK0vetdwv_H2MHj4ewTfZGtSbrbSNDaV5xJhNo_Hdp8';
|
return 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzQyMDM5MzU4LCJleHAiOjQ4OTU2MzkzNTh9.XK0vetdwv_H2MHj4ewTfZGtSbrbSNDaV5xJhNo_Hdp8';
|
||||||
};
|
};
|
||||||
|
|
||||||
// CORS 프록시 사용 여부 설정
|
// CORS 프록시 사용 여부 설정
|
||||||
|
|||||||
@@ -1,108 +1,96 @@
|
|||||||
|
|
||||||
import { getSupabaseUrl, getSupabaseKey, getOriginalSupabaseUrl, isCorsProxyEnabled, getProxyType } from '../config';
|
|
||||||
import { testAuthService } from './authTests';
|
import { testAuthService } from './authTests';
|
||||||
import { testRestApi } from './apiTests';
|
import { testRestApi } from './apiTests';
|
||||||
import { testDatabaseConnection } from './databaseTests';
|
import { testDatabaseConnection } from './databaseTests';
|
||||||
import { ConnectionTestResult, LoginTestResult } from './types';
|
import { ConnectionTestResult, TestDebugInfo } from './types';
|
||||||
|
import { supabase } from '../client';
|
||||||
|
import { getOriginalSupabaseUrl, getSupabaseUrl, isCorsProxyEnabled, getProxyType } from '../config';
|
||||||
|
|
||||||
export { testSupabaseLogin } from './authTests';
|
// 기본 테스트 함수
|
||||||
|
|
||||||
// API 테스트 도우미 함수
|
|
||||||
export const testSupabaseConnection = async (): Promise<ConnectionTestResult> => {
|
export const testSupabaseConnection = async (): Promise<ConnectionTestResult> => {
|
||||||
|
// 브라우저 및 환경 정보 수집
|
||||||
|
const browserInfo = `${navigator.userAgent}`;
|
||||||
const originalUrl = getOriginalSupabaseUrl();
|
const originalUrl = getOriginalSupabaseUrl();
|
||||||
const proxyUrl = getSupabaseUrl();
|
const proxyUrl = getSupabaseUrl();
|
||||||
const usingProxy = isCorsProxyEnabled();
|
const usingProxy = isCorsProxyEnabled();
|
||||||
const proxyType = getProxyType();
|
const proxyType = getProxyType();
|
||||||
const supabaseKey = getSupabaseKey();
|
|
||||||
|
|
||||||
const results: ConnectionTestResult = {
|
// 디버그 정보 초기화
|
||||||
url: originalUrl, // 원본 URL
|
const debugInfo: TestDebugInfo = {
|
||||||
proxyUrl: proxyUrl, // 프록시 적용된 URL
|
|
||||||
usingProxy: usingProxy, // 프록시 사용 여부
|
|
||||||
proxyType: proxyType, // 프록시 유형
|
|
||||||
client: !!supabase,
|
|
||||||
restApi: false,
|
|
||||||
auth: false,
|
|
||||||
database: false,
|
|
||||||
errors: [] as string[],
|
|
||||||
debugInfo: {
|
|
||||||
originalUrl,
|
originalUrl,
|
||||||
proxyUrl,
|
proxyUrl,
|
||||||
usingProxy,
|
usingProxy,
|
||||||
proxyType,
|
proxyType,
|
||||||
keyLength: supabaseKey.length,
|
keyLength: localStorage.getItem('supabase_key')?.length || 0,
|
||||||
browserInfo: navigator.userAgent,
|
browserInfo,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
backupProxySuccess: false,
|
backupProxySuccess: false,
|
||||||
lastErrorDetails: ''
|
lastErrorDetails: '',
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('연결 테스트 시작 - 설정 정보:', {
|
// 테스트 결과 초기화
|
||||||
originalUrl,
|
const result: ConnectionTestResult = {
|
||||||
|
url: originalUrl,
|
||||||
proxyUrl,
|
proxyUrl,
|
||||||
usingProxy,
|
usingProxy,
|
||||||
proxyType
|
proxyType,
|
||||||
});
|
client: false,
|
||||||
|
restApi: false,
|
||||||
try {
|
auth: false,
|
||||||
// 1. REST API 테스트
|
database: false,
|
||||||
try {
|
errors: [],
|
||||||
const apiTestResult = await testRestApi(proxyUrl, originalUrl, supabaseKey, proxyType);
|
debugInfo
|
||||||
results.restApi = apiTestResult.success;
|
|
||||||
|
|
||||||
if (!apiTestResult.success) {
|
|
||||||
results.debugInfo.lastErrorDetails = apiTestResult.lastErrorDetails || '';
|
|
||||||
results.debugInfo.backupProxySuccess = !!apiTestResult.backupProxySuccess;
|
|
||||||
|
|
||||||
if (apiTestResult.backupProxySuccess && apiTestResult.recommendedProxy) {
|
|
||||||
results.errors.push(`REST API 성공: ${apiTestResult.recommendedProxy} 프록시 사용 시 정상 작동합니다. 설정에서 이 프록시를 선택해보세요.`);
|
|
||||||
} else if (usingProxy) {
|
|
||||||
results.errors.push(`REST API 오류: ${apiTestResult.lastErrorDetails || '응답 없음'} - 다른 프록시 옵션도 모두 실패했습니다.`);
|
|
||||||
} else {
|
|
||||||
results.errors.push(`REST API 오류: ${apiTestResult.lastErrorDetails || '응답 없음'} - CORS 프록시 활성화를 시도해보세요.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
results.errors.push(`REST API 예외: ${err.message || '알 수 없는 오류'}`);
|
|
||||||
console.error('REST API 테스트 중 예외:', err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 인증 서비스 테스트
|
|
||||||
try {
|
|
||||||
const authTestResult = await testAuthService();
|
|
||||||
results.auth = authTestResult.success;
|
|
||||||
|
|
||||||
if (!authTestResult.success) {
|
|
||||||
results.errors.push(`인증 오류: ${authTestResult.error?.message || '알 수 없는 오류'}`);
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
results.errors.push(`인증 예외: ${err.message || '알 수 없는 오류'}`);
|
|
||||||
console.error('인증 테스트 중 예외:', err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 데이터베이스 연결 테스트
|
|
||||||
try {
|
|
||||||
const dbTestResult = await testDatabaseConnection();
|
|
||||||
results.database = dbTestResult.success;
|
|
||||||
|
|
||||||
if (!dbTestResult.success) {
|
|
||||||
results.errors.push(`데이터베이스 오류: ${dbTestResult.error?.message || '알 수 없는 오류'}`);
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
results.errors.push(`데이터베이스 예외: ${err.message || '알 수 없는 오류'}`);
|
|
||||||
console.error('데이터베이스 테스트 중 예외:', err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 오류가 없는 경우 메시지 추가
|
|
||||||
if (results.errors.length === 0) {
|
|
||||||
results.errors.push('모든 테스트 통과! 연결 상태가 정상입니다.');
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
results.errors.push(`테스트 실행 예외: ${err.message || '알 수 없는 오류'}`);
|
|
||||||
console.error('전체 테스트 실행 중 예외:', err);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Supabase 연결 테스트 결과:', results);
|
|
||||||
return results;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 클라이언트 초기화 테스트
|
||||||
|
if (supabase) {
|
||||||
|
result.client = true;
|
||||||
|
} else {
|
||||||
|
result.errors.push('Supabase 클라이언트 초기화 실패');
|
||||||
|
}
|
||||||
|
|
||||||
|
// REST API 테스트
|
||||||
|
const apiTest = await testRestApi();
|
||||||
|
result.restApi = apiTest.success;
|
||||||
|
if (!apiTest.success && apiTest.error) {
|
||||||
|
result.errors.push(`REST API 오류: ${apiTest.error.message || '알 수 없는 오류'}`);
|
||||||
|
debugInfo.lastErrorDetails = JSON.stringify(apiTest.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 인증 테스트
|
||||||
|
const authTest = await testAuthService();
|
||||||
|
result.auth = authTest.success;
|
||||||
|
if (!authTest.success && authTest.error) {
|
||||||
|
result.errors.push(`인증 오류: ${authTest.error.message || '알 수 없는 오류'}`);
|
||||||
|
if (!debugInfo.lastErrorDetails) {
|
||||||
|
debugInfo.lastErrorDetails = JSON.stringify(authTest.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 데이터베이스 테스트
|
||||||
|
const dbTest = await testDatabaseConnection();
|
||||||
|
result.database = dbTest.success;
|
||||||
|
if (!dbTest.success && dbTest.error) {
|
||||||
|
result.errors.push(`데이터베이스 오류: ${dbTest.error.message || '알 수 없는 오류'}`);
|
||||||
|
if (!debugInfo.lastErrorDetails) {
|
||||||
|
debugInfo.lastErrorDetails = JSON.stringify(dbTest.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 모든 테스트 실패 시 백업 프록시 테스트
|
||||||
|
if (usingProxy && !result.restApi && !result.auth && !result.database) {
|
||||||
|
console.log('모든 테스트가 실패했습니다. 다른 프록시 테스트를 시도합니다...');
|
||||||
|
debugInfo.backupProxySuccess = false; // 백업 프록시 테스트 결과 초기화
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Supabase 연결 테스트 중 예외 발생:', err);
|
||||||
|
result.errors.push(`테스트 중 예외 발생: ${err instanceof Error ? err.message : '알 수 없는 오류'}`);
|
||||||
|
debugInfo.lastErrorDetails = err instanceof Error ? err.stack || err.message : String(err);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export { testAuthService, testRestApi, testDatabaseConnection };
|
||||||
|
|||||||
Reference in New Issue
Block a user