Remove on-premise Supabase test code
Remove code related to on-premise Supabase testing.
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { CloudUpload, RefreshCw, Database } from "lucide-react";
|
import { CloudUpload, RefreshCw } from "lucide-react";
|
||||||
import { isSyncEnabled, setSyncEnabled, syncAllData, getLastSyncTime } from "@/utils/syncUtils";
|
import { isSyncEnabled, setSyncEnabled, syncAllData, getLastSyncTime } from "@/utils/syncUtils";
|
||||||
import { toast } from "@/hooks/useToast.wrapper";
|
import { toast } from "@/hooks/useToast.wrapper";
|
||||||
import { useAuth } from "@/contexts/auth";
|
import { useAuth } from "@/contexts/auth";
|
||||||
@@ -139,18 +139,6 @@ const SyncSettings = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 온프레미스 Supabase 설정 버튼 추가 */}
|
|
||||||
<div className="pt-4 border-t border-gray-200">
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => navigate('/supabase-settings')}
|
|
||||||
className="w-full flex items-center justify-center gap-2"
|
|
||||||
>
|
|
||||||
<Database className="h-4 w-4" />
|
|
||||||
<span>온프레미스 Supabase 설정</span>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
|
|
||||||
import React from "react";
|
|
||||||
import { RefreshCcw } from "lucide-react";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert";
|
|
||||||
|
|
||||||
interface ServerStatusAlertProps {
|
|
||||||
serverStatus: {
|
|
||||||
checked: boolean;
|
|
||||||
connected: boolean;
|
|
||||||
message: string;
|
|
||||||
};
|
|
||||||
checkServerConnection: () => Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ServerStatusAlert: React.FC<ServerStatusAlertProps> = ({
|
|
||||||
serverStatus,
|
|
||||||
checkServerConnection,
|
|
||||||
}) => {
|
|
||||||
if (!serverStatus.checked || serverStatus.connected) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Alert variant="destructive" className="mb-6">
|
|
||||||
<AlertTitle className="flex items-center">
|
|
||||||
서버 연결 문제
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
className="ml-2 h-6 w-6 p-0"
|
|
||||||
onClick={checkServerConnection}
|
|
||||||
>
|
|
||||||
<RefreshCcw className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</AlertTitle>
|
|
||||||
<AlertDescription>
|
|
||||||
{serverStatus.message}
|
|
||||||
<p className="mt-1 text-xs">회원가입을 시도하기 전에 서버 연결 문제를 해결해주세요.</p>
|
|
||||||
</AlertDescription>
|
|
||||||
</Alert>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ServerStatusAlert;
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
|
|
||||||
import React from "react";
|
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
import { ArrowRight, Shield } from "lucide-react";
|
|
||||||
import { TestResults } from "@/lib/supabase/tests/types";
|
|
||||||
|
|
||||||
interface TestResultProps {
|
|
||||||
testResults: TestResults | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SupabaseConnectionStatus: React.FC<TestResultProps> = ({ testResults }) => {
|
|
||||||
if (!testResults) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<details className="neuro-flat p-3 text-xs mb-4">
|
|
||||||
<summary className="cursor-pointer text-gray-500 font-medium">고급 진단 정보</summary>
|
|
||||||
<div className="mt-2 space-y-2">
|
|
||||||
<div>
|
|
||||||
<p className="font-medium">Supabase URL:</p>
|
|
||||||
<p className="break-all bg-gray-100 p-1 rounded">{testResults?.url || '알 수 없음'}</p>
|
|
||||||
</div>
|
|
||||||
{testResults?.usingProxy && (
|
|
||||||
<div>
|
|
||||||
<p className="font-medium">CORS 프록시:</p>
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<Shield className="h-3 w-3 text-blue-500" />
|
|
||||||
<p className="break-all text-blue-500">활성화됨 - {testResults.proxyUrl}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div>
|
|
||||||
<p className="font-medium">클라이언트 초기화:</p>
|
|
||||||
<p>{testResults?.client ? '성공' : '실패'}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="font-medium">브라우저 정보:</p>
|
|
||||||
<p className="break-all">{navigator.userAgent}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="font-medium">앱 버전:</p>
|
|
||||||
<p>1.0.0</p>
|
|
||||||
</div>
|
|
||||||
{testResults && (
|
|
||||||
<div className="border-t pt-2 mt-2">
|
|
||||||
<p>REST API: {testResults.restApi ? '✅' : '❌'}</p>
|
|
||||||
<p>인증: {testResults.auth ? '✅' : '❌'}</p>
|
|
||||||
<p>데이터베이스: {testResults.database ? '✅' : '❌'}</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="text-right pt-1">
|
|
||||||
<Link
|
|
||||||
to="/supabase-settings"
|
|
||||||
className="inline-flex items-center text-neuro-income hover:underline"
|
|
||||||
>
|
|
||||||
<span>Supabase 설정 변경</span>
|
|
||||||
<ArrowRight className="h-3 w-3 ml-1" />
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</details>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SupabaseConnectionStatus;
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
|
|
||||||
import React, { useState } from "react";
|
|
||||||
import { RefreshCw } from "lucide-react";
|
|
||||||
import { testSupabaseConnection } from "@/lib/supabase";
|
|
||||||
import { useToast } from "@/hooks/useToast.wrapper";
|
|
||||||
import { TestResults } from "@/lib/supabase/tests/types";
|
|
||||||
|
|
||||||
interface TestConnectionSectionProps {
|
|
||||||
setLoginError: (error: string | null) => void;
|
|
||||||
setTestResults: (results: TestResults) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TestConnectionSection: React.FC<TestConnectionSectionProps> = ({
|
|
||||||
setLoginError,
|
|
||||||
setTestResults
|
|
||||||
}) => {
|
|
||||||
const [testLoading, setTestLoading] = useState(false);
|
|
||||||
const { toast } = useToast();
|
|
||||||
|
|
||||||
const runConnectionTest = async () => {
|
|
||||||
setTestLoading(true);
|
|
||||||
setLoginError(null);
|
|
||||||
try {
|
|
||||||
const results = await testSupabaseConnection();
|
|
||||||
setTestResults(results);
|
|
||||||
|
|
||||||
if (results.errors.length === 0) {
|
|
||||||
toast({
|
|
||||||
title: "연결 테스트 성공",
|
|
||||||
description: "Supabase 서버 연결이 정상입니다.",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
toast({
|
|
||||||
title: "연결 테스트 실패",
|
|
||||||
description: `오류 발생: ${results.errors[0]}`,
|
|
||||||
variant: "destructive"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error("연결 테스트 중 오류:", error);
|
|
||||||
setLoginError(error.message || "테스트 실행 중 예상치 못한 오류가 발생했습니다.");
|
|
||||||
|
|
||||||
toast({
|
|
||||||
title: "연결 테스트 오류",
|
|
||||||
description: "테스트 실행 중 예상치 못한 오류가 발생했습니다.",
|
|
||||||
variant: "destructive"
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
setTestLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="neuro-card p-4 mb-6">
|
|
||||||
<h3 className="text-sm font-medium mb-2">Supabase 연결 진단</h3>
|
|
||||||
<div className="flex justify-between items-center">
|
|
||||||
<button
|
|
||||||
onClick={runConnectionTest}
|
|
||||||
disabled={testLoading}
|
|
||||||
className="text-xs flex items-center text-neuro-income hover:underline"
|
|
||||||
>
|
|
||||||
{testLoading ? (
|
|
||||||
<>
|
|
||||||
<RefreshCw className="mr-1 h-3 w-3 animate-spin" />
|
|
||||||
테스트 중...
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<RefreshCw className="mr-1 h-3 w-3" />
|
|
||||||
연결 테스트 실행
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TestConnectionSection;
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
|
|
||||||
// 서버 연결 상태 타입
|
|
||||||
export interface ServerStatus {
|
|
||||||
checked: boolean;
|
|
||||||
connected: boolean;
|
|
||||||
message: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 회원가입 응답 타입
|
|
||||||
export interface SignUpResponse {
|
|
||||||
error: any;
|
|
||||||
user: any;
|
|
||||||
redirectToSettings?: boolean;
|
|
||||||
emailConfirmationRequired?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 회원가입 폼 공통 props
|
|
||||||
export interface RegisterFormCommonProps {
|
|
||||||
serverStatus: ServerStatus;
|
|
||||||
setRegisterError: React.Dispatch<React.SetStateAction<string | null>>;
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
|
|
||||||
import React from 'react';
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { RefreshCw } from "lucide-react";
|
|
||||||
|
|
||||||
interface ConnectionTestButtonProps {
|
|
||||||
onClick: () => void;
|
|
||||||
isTesting: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ConnectionTestButton: React.FC<ConnectionTestButtonProps> = ({ onClick, isTesting }) => {
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
onClick={onClick}
|
|
||||||
disabled={isTesting}
|
|
||||||
variant="outline"
|
|
||||||
className="w-full mb-4"
|
|
||||||
>
|
|
||||||
{isTesting ? (
|
|
||||||
<>
|
|
||||||
<RefreshCw className="mr-2 h-4 w-4 animate-spin" />
|
|
||||||
테스트 중...
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<RefreshCw className="mr-2 h-4 w-4" />
|
|
||||||
연결 테스트 실행
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ConnectionTestButton;
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
|
|
||||||
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;
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
|
|
||||||
import React from 'react';
|
|
||||||
import { TestResults } from '@/lib/supabase/tests/types';
|
|
||||||
|
|
||||||
interface ProxyInfoCardProps {
|
|
||||||
testResults: TestResults;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ProxyInfoCard: React.FC<ProxyInfoCardProps> = ({ testResults }) => {
|
|
||||||
if (!testResults.usingProxy) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="bg-blue-50 border border-blue-200 rounded p-2 mt-2">
|
|
||||||
<p className="text-xs">CORS 프록시 사용 중: {testResults.proxyType || 'corsproxy.io'}</p>
|
|
||||||
<p className="text-xs break-all mt-1">{testResults.proxyUrl}</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ProxyInfoCard;
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
|
|
||||||
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="cloudflare">Cloudflare Workers 프록시 (기본)</SelectItem>
|
|
||||||
<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">
|
|
||||||
Cloudflare 프록시가 가장 안정적인 성능을 제공합니다.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ProxyTypeSelector;
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
|
|
||||||
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;
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
|
|
||||||
import React, { useState } from 'react';
|
|
||||||
import { toast } from "@/hooks/useToast.wrapper";
|
|
||||||
import { testSupabaseConnection } from '@/lib/supabase';
|
|
||||||
import { TestResults } from '@/lib/supabase/tests/types';
|
|
||||||
|
|
||||||
// 분리된 컴포넌트들 임포트
|
|
||||||
import ConnectionTestButton from './ConnectionTestButton';
|
|
||||||
import TestResultItem from './TestResultItem';
|
|
||||||
import ProxyInfoCard from './ProxyInfoCard';
|
|
||||||
import ProxyRecommendationAlert from './ProxyRecommendationAlert';
|
|
||||||
import ErrorMessageCard from './ErrorMessageCard';
|
|
||||||
import TroubleshootingTips from './TroubleshootingTips';
|
|
||||||
import DebugInfoCollapsible from './DebugInfoCollapsible';
|
|
||||||
|
|
||||||
const SupabaseConnectionTest: React.FC = () => {
|
|
||||||
const [testResults, setTestResults] = useState<TestResults | null>(null);
|
|
||||||
const [isTesting, setIsTesting] = useState(false);
|
|
||||||
const [showDebug, setShowDebug] = useState(false);
|
|
||||||
|
|
||||||
const runConnectionTest = async () => {
|
|
||||||
setIsTesting(true);
|
|
||||||
try {
|
|
||||||
const results = await testSupabaseConnection();
|
|
||||||
setTestResults(results);
|
|
||||||
|
|
||||||
if (results.errors.length === 0 || (results.auth && results.restApi && results.database)) {
|
|
||||||
toast({
|
|
||||||
title: "연결 테스트 성공",
|
|
||||||
description: "Supabase 서버 연결이 정상입니다.",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
toast({
|
|
||||||
title: "연결 테스트 실패",
|
|
||||||
description: `오류 발생: ${results.errors[0]}`,
|
|
||||||
variant: "destructive"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error("연결 테스트 중 오류:", error);
|
|
||||||
toast({
|
|
||||||
title: "연결 테스트 오류",
|
|
||||||
description: "테스트 실행 중 예상치 못한 오류가 발생했습니다.",
|
|
||||||
variant: "destructive"
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
setIsTesting(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h2 className="text-lg font-semibold mb-4">연결 테스트</h2>
|
|
||||||
|
|
||||||
<ConnectionTestButton
|
|
||||||
onClick={runConnectionTest}
|
|
||||||
isTesting={isTesting}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{testResults && (
|
|
||||||
<div className="mt-4 space-y-3 text-sm">
|
|
||||||
<TestResultItem label="REST API" success={testResults.restApi} />
|
|
||||||
<TestResultItem label="인증" success={testResults.auth} />
|
|
||||||
<TestResultItem label="데이터베이스" success={testResults.database} />
|
|
||||||
|
|
||||||
<ProxyInfoCard testResults={testResults} />
|
|
||||||
|
|
||||||
<ProxyRecommendationAlert errors={testResults.errors} />
|
|
||||||
|
|
||||||
<ErrorMessageCard errors={testResults.errors} />
|
|
||||||
|
|
||||||
<TroubleshootingTips testResults={testResults} />
|
|
||||||
|
|
||||||
<DebugInfoCollapsible
|
|
||||||
testResults={testResults}
|
|
||||||
showDebug={showDebug}
|
|
||||||
setShowDebug={setShowDebug}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SupabaseConnectionTest;
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
|
|
||||||
import React from 'react';
|
|
||||||
import { AlertCircle } from 'lucide-react';
|
|
||||||
|
|
||||||
const SupabaseHelpSection: React.FC = () => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h2 className="text-lg font-semibold mb-2">도움말</h2>
|
|
||||||
<p className="text-gray-500 text-sm mb-4">
|
|
||||||
온프레미스 Supabase 설정은 다음과 같이 구성됩니다:
|
|
||||||
</p>
|
|
||||||
<ul className="list-disc pl-5 text-sm text-gray-500 space-y-2">
|
|
||||||
<li>
|
|
||||||
<strong>Supabase URL</strong>: 온프레미스로 설치한 Supabase 인스턴스의 URL을 입력합니다.
|
|
||||||
(예: https://192.168.1.100:8000)
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Anon Key</strong>: Supabase 대시보드의 API 설정에서 찾을 수 있는 anon/public 키를 입력합니다.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>CORS 프록시</strong>: HTTP URL에 대한 브라우저 보안 제한을 우회하기 위해 사용합니다.
|
|
||||||
HTTPS가 아닌 URL에 접근 문제가 있는 경우 활성화하세요.
|
|
||||||
</li>
|
|
||||||
<li className="text-amber-500 font-medium">
|
|
||||||
가능하면 HTTPS URL을 사용하는 것이 좋습니다. HTTP URL을 사용해야 하는 경우 CORS 프록시를 활성화하세요.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
{/* 오류 해결 팁 추가 */}
|
|
||||||
<div className="mt-6 border-t pt-4">
|
|
||||||
<h3 className="flex items-center text-sm font-semibold mb-2">
|
|
||||||
<AlertCircle className="h-4 w-4 mr-1 text-amber-500" />
|
|
||||||
<span>연결 문제 해결 팁</span>
|
|
||||||
</h3>
|
|
||||||
<ul className="list-disc pl-5 text-xs text-gray-500 space-y-2">
|
|
||||||
<li>
|
|
||||||
<strong>REST API 오류</strong>: 서버의 CORS 설정을 확인해보세요. Supabase 서버에서 CORS 허용 설정이 필요할 수 있습니다.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>인증 성공, API 실패</strong>: 이 경우는 일반적으로 CORS 문제를 의미합니다. CORS 프록시를 활성화하고 서버 설정도 확인해보세요.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>데이터베이스 오류</strong>: Supabase 관리자 대시보드에서 해당 테이블이 존재하는지, 그리고 익명 접근 권한이 있는지 확인하세요.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>HTTPS 필요</strong>: 로컬 개발 환경에서는 HTTP 접근이 가능하지만, 배포된 앱에서는 HTTPS Supabase URL이 필요할 수 있습니다.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SupabaseHelpSection;
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
|
|
||||||
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,137 +0,0 @@
|
|||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import { toast } from "@/hooks/useToast.wrapper";
|
|
||||||
import {
|
|
||||||
configureSupabase,
|
|
||||||
isCorsProxyEnabled,
|
|
||||||
getProxyType
|
|
||||||
} from "@/lib/supabase/config";
|
|
||||||
|
|
||||||
// 분리된 컴포넌트들 임포트
|
|
||||||
import SupabaseUrlInput from './SupabaseUrlInput';
|
|
||||||
import SupabaseKeyInput from './SupabaseKeyInput';
|
|
||||||
import CorsProxyToggle from './CorsProxyToggle';
|
|
||||||
import ProxyTypeSelector from './ProxyTypeSelector';
|
|
||||||
import SaveSettingsButton from './SaveSettingsButton';
|
|
||||||
|
|
||||||
interface SupabaseSettingsFormProps {
|
|
||||||
onSaved: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SupabaseSettingsForm: React.FC<SupabaseSettingsFormProps> = ({ onSaved }) => {
|
|
||||||
// 상태 관리
|
|
||||||
const [formState, setFormState] = useState({
|
|
||||||
supabaseUrl: '',
|
|
||||||
supabaseKey: '',
|
|
||||||
useProxy: true, // 기본값을 true로 변경
|
|
||||||
proxyType: 'cloudflare', // 기본값을 cloudflare로 변경
|
|
||||||
isSaving: false
|
|
||||||
});
|
|
||||||
|
|
||||||
// 상태 업데이트 핸들러
|
|
||||||
const updateState = (update: Partial<typeof formState>) => {
|
|
||||||
setFormState(prev => ({ ...prev, ...update }));
|
|
||||||
};
|
|
||||||
|
|
||||||
// 저장된 설정 불러오기
|
|
||||||
useEffect(() => {
|
|
||||||
const savedUrl = localStorage.getItem('supabase_url');
|
|
||||||
const savedKey = localStorage.getItem('supabase_key');
|
|
||||||
const proxyEnabled = localStorage.getItem('use_cors_proxy') === 'true';
|
|
||||||
const savedProxyType = getProxyType();
|
|
||||||
|
|
||||||
setFormState(prev => ({
|
|
||||||
...prev,
|
|
||||||
supabaseUrl: savedUrl || '',
|
|
||||||
supabaseKey: savedKey || '',
|
|
||||||
useProxy: proxyEnabled === false ? false : true, // 저장된 값이 명시적으로 false인 경우에만 false, 아니면 기본값 true
|
|
||||||
proxyType: savedProxyType || 'cloudflare'
|
|
||||||
}));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// URL 유효성 검사
|
|
||||||
const validateUrl = (url: string): boolean => {
|
|
||||||
return /^https?:\/\/.+/.test(url);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 폼 제출 및 설정 저장
|
|
||||||
const handleSave = () => {
|
|
||||||
const { supabaseUrl, supabaseKey, useProxy, proxyType } = formState;
|
|
||||||
|
|
||||||
// 입력값 검증
|
|
||||||
if (!supabaseUrl || !supabaseKey) {
|
|
||||||
toast({
|
|
||||||
title: "입력 오류",
|
|
||||||
description: "Supabase URL과 Anon Key를 모두 입력해주세요.",
|
|
||||||
variant: "destructive"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!validateUrl(supabaseUrl)) {
|
|
||||||
toast({
|
|
||||||
title: "URL 오류",
|
|
||||||
description: "유효한 URL 형식이 아닙니다. http:// 또는 https://로 시작하는 URL을 입력해주세요.",
|
|
||||||
variant: "destructive"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 저장 처리
|
|
||||||
updateState({ isSaving: true });
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Supabase 설정 적용
|
|
||||||
configureSupabase(supabaseUrl, supabaseKey, useProxy, proxyType);
|
|
||||||
|
|
||||||
toast({
|
|
||||||
title: "설정 저장 완료",
|
|
||||||
description: "Supabase 설정이 저장되었습니다. 변경사항을 적용합니다.",
|
|
||||||
});
|
|
||||||
|
|
||||||
onSaved();
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Supabase 설정 저장 오류:', error);
|
|
||||||
toast({
|
|
||||||
title: "설정 저장 실패",
|
|
||||||
description: "Supabase 설정을 저장하는 중 오류가 발생했습니다.",
|
|
||||||
variant: "destructive"
|
|
||||||
});
|
|
||||||
updateState({ isSaving: false });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="space-y-6">
|
|
||||||
<SupabaseUrlInput
|
|
||||||
supabaseUrl={formState.supabaseUrl}
|
|
||||||
setSupabaseUrl={(url) => updateState({ supabaseUrl: url })}
|
|
||||||
useProxy={formState.useProxy}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SupabaseKeyInput
|
|
||||||
supabaseKey={formState.supabaseKey}
|
|
||||||
setSupabaseKey={(key) => updateState({ supabaseKey: key })}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<CorsProxyToggle
|
|
||||||
useProxy={formState.useProxy}
|
|
||||||
setUseProxy={(value) => updateState({ useProxy: value })}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{formState.useProxy && (
|
|
||||||
<ProxyTypeSelector
|
|
||||||
proxyType={formState.proxyType}
|
|
||||||
setProxyType={(type) => updateState({ proxyType: type })}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<SaveSettingsButton
|
|
||||||
onClick={handleSave}
|
|
||||||
isSaving={formState.isSaving}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SupabaseSettingsForm;
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
|
|
||||||
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,20 +0,0 @@
|
|||||||
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
interface TestResultItemProps {
|
|
||||||
label: string;
|
|
||||||
success: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TestResultItem: React.FC<TestResultItemProps> = ({ label, success }) => {
|
|
||||||
return (
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<span className="font-medium">{label}:</span>
|
|
||||||
<span className={success ? 'text-green-500' : 'text-red-500'}>
|
|
||||||
{success ? '✅ 성공' : '❌ 실패'}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TestResultItem;
|
|
||||||
@@ -18,14 +18,7 @@ try {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Supabase Cloud 클라이언트가 생성되었습니다.');
|
console.log('Supabase 클라이언트가 생성되었습니다.');
|
||||||
|
|
||||||
// 연결 테스트 - 별도 파일로 이동됨
|
|
||||||
import('./connectionTest').then(module => {
|
|
||||||
module.testConnection(supabaseClient, supabaseUrl, supabaseAnonKey);
|
|
||||||
}).catch(err => {
|
|
||||||
console.error('연결 테스트 모듈 로딩 오류:', err);
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Supabase 클라이언트 생성 오류:', error);
|
console.error('Supabase 클라이언트 생성 오류:', error);
|
||||||
|
|||||||
@@ -1,17 +1,10 @@
|
|||||||
|
|
||||||
// Supabase Cloud URL과 anon key 설정 (고정값 사용)
|
// Supabase Cloud URL과 anon key 설정
|
||||||
export const getSupabaseUrl = () => {
|
export const getSupabaseUrl = () => {
|
||||||
// Supabase Cloud URL 사용
|
|
||||||
return "https://qnerebtvwwfobfzdoftx.supabase.co";
|
|
||||||
};
|
|
||||||
|
|
||||||
// 원본 URL 반환
|
|
||||||
export const getOriginalSupabaseUrl = () => {
|
|
||||||
return "https://qnerebtvwwfobfzdoftx.supabase.co";
|
return "https://qnerebtvwwfobfzdoftx.supabase.co";
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getSupabaseKey = () => {
|
export const getSupabaseKey = () => {
|
||||||
// Supabase Cloud anon key 사용
|
|
||||||
return "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InFuZXJlYnR2d3dmb2JmemRvZnR4Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDIwNTE0MzgsImV4cCI6MjA1NzYyNzQzOH0.Wm7h2DUhoQbeANuEM3wm2tz22ITrVEW8FizyLgIVmv8";
|
return "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InFuZXJlYnR2d3dmb2JmemRvZnR4Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDIwNTE0MzgsImV4cCI6MjA1NzYyNzQzOH0.Wm7h2DUhoQbeANuEM3wm2tz22ITrVEW8FizyLgIVmv8";
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -19,29 +12,3 @@ export const getSupabaseKey = () => {
|
|||||||
export const isValidSupabaseKey = () => {
|
export const isValidSupabaseKey = () => {
|
||||||
return true; // Supabase Cloud에서는 항상 유효함
|
return true; // Supabase Cloud에서는 항상 유효함
|
||||||
};
|
};
|
||||||
|
|
||||||
// CORS 프록시 관련 함수들 (Supabase Cloud에서는 필요 없음)
|
|
||||||
export const useCorsProxy = (enabled: boolean) => {
|
|
||||||
return false; // Supabase Cloud에서는 항상 비활성화
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setProxyType = (proxyType: string) => {
|
|
||||||
// Supabase Cloud에서는 아무 작업도 하지 않음
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getProxyType = () => {
|
|
||||||
return 'none'; // Supabase Cloud에서는 프록시 사용 안함
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isCorsProxyEnabled = () => {
|
|
||||||
return false; // Supabase Cloud에서는 항상 false
|
|
||||||
};
|
|
||||||
|
|
||||||
// 구성 도우미 함수 - Cloud 환경에서는 단순화
|
|
||||||
export const configureSupabase = (url: string, key: string, useProxy: boolean = false, proxyType: string = 'none') => {
|
|
||||||
console.log('Supabase Cloud를 사용 중이므로 설정이 무시됩니다');
|
|
||||||
// 실제 설정은 변경되지 않음 (Cloud URL 및 키는 고정)
|
|
||||||
|
|
||||||
// 페이지 새로고침
|
|
||||||
window.location.reload();
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
|
|
||||||
import { SupabaseClient } from '@supabase/supabase-js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Supabase 연결 테스트
|
|
||||||
*/
|
|
||||||
export async function testConnection(
|
|
||||||
supabaseClient: SupabaseClient,
|
|
||||||
supabaseUrl: string,
|
|
||||||
supabaseAnonKey: string
|
|
||||||
): Promise<void> {
|
|
||||||
// CORS 문제 확인을 위한 기본 헤더 테스트
|
|
||||||
try {
|
|
||||||
// 기본 서버 상태 확인 (CORS 테스트)
|
|
||||||
console.log('Supabase 서버 상태 확인 중...');
|
|
||||||
const response = await fetch(`${supabaseUrl}/auth/v1/`, {
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'apikey': supabaseAnonKey,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok || response.status === 401 || response.status === 404) {
|
|
||||||
console.log('Supabase 서버 연결 성공:', response.status);
|
|
||||||
} else {
|
|
||||||
console.warn('Supabase 서버 연결 실패:', response.status, response.statusText);
|
|
||||||
// 응답 세부 정보 로깅
|
|
||||||
try {
|
|
||||||
const errorText = await response.text();
|
|
||||||
console.warn('Supabase 서버 응답 내용:', errorText);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('응답 내용 읽기 실패:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Supabase 서버 상태 확인 중 오류 (CORS 문제 가능성):', err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Supabase 연결 테스트
|
|
||||||
try {
|
|
||||||
console.log('Supabase 인증 테스트 시도 중...');
|
|
||||||
const { data, error } = await supabaseClient.auth.getSession();
|
|
||||||
if (error) {
|
|
||||||
console.warn('Supabase 연결 테스트 실패:', error.message);
|
|
||||||
} else {
|
|
||||||
console.log('Supabase 연결 성공!', data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 추가 테스트: 공개 데이터 조회 시도
|
|
||||||
try {
|
|
||||||
console.log('Supabase 데이터베이스 공개 테이블 조회 시도...');
|
|
||||||
const { data: tableData, error: tableError } = await supabaseClient
|
|
||||||
.from('transactions')
|
|
||||||
.select('*')
|
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
if (tableError) {
|
|
||||||
console.warn('Supabase 데이터베이스 테스트 실패:', tableError.message);
|
|
||||||
} else {
|
|
||||||
console.log('Supabase 데이터베이스 테스트 성공:', tableData);
|
|
||||||
}
|
|
||||||
} catch (dbErr) {
|
|
||||||
console.error('Supabase 데이터베이스 테스트 중 예외 발생:', dbErr);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Supabase 연결 확인 중 오류:', err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +1,15 @@
|
|||||||
|
|
||||||
import { supabase, isValidUrl } from './client';
|
import { supabase, isValidUrl } from './client';
|
||||||
import { testSupabaseConnection } from './tests';
|
|
||||||
import { createRequiredTables, checkTablesStatus } from './setup';
|
import { createRequiredTables, checkTablesStatus } from './setup';
|
||||||
import { customFetch } from './customFetch';
|
import { customFetch } from './customFetch';
|
||||||
import { modifyStorageApiRequest, getStorageApiHeaders } from './storageUtils';
|
import { modifyStorageApiRequest, getStorageApiHeaders } from './storageUtils';
|
||||||
import { testConnection } from './connectionTest';
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
supabase,
|
supabase,
|
||||||
isValidUrl,
|
isValidUrl,
|
||||||
testSupabaseConnection,
|
|
||||||
createRequiredTables,
|
createRequiredTables,
|
||||||
checkTablesStatus,
|
checkTablesStatus,
|
||||||
customFetch,
|
customFetch,
|
||||||
modifyStorageApiRequest,
|
modifyStorageApiRequest,
|
||||||
getStorageApiHeaders,
|
getStorageApiHeaders
|
||||||
testConnection
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const createRequiredTables = async (): Promise<{ success: boolean; messag
|
|||||||
// 테이블 상태 확인
|
// 테이블 상태 확인
|
||||||
const tablesStatus = await checkTablesStatus();
|
const tablesStatus = await checkTablesStatus();
|
||||||
|
|
||||||
if (tablesStatus.transactions && tablesStatus.budgets) {
|
if (tablesStatus.transactions && tablesStatus.budgets && tablesStatus.category_budgets) {
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: '필요한 테이블이 이미 존재합니다.'
|
message: '필요한 테이블이 이미 존재합니다.'
|
||||||
@@ -21,8 +21,8 @@ export const createRequiredTables = async (): Promise<{ success: boolean; messag
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: false,
|
||||||
message: '테이블이 성공적으로 생성되었습니다.'
|
message: '일부 필요한 테이블이 없습니다. Supabase 대시보드에서 확인해주세요.'
|
||||||
};
|
};
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('테이블 확인 중 오류 발생:', error);
|
console.error('테이블 확인 중 오류 발생:', error);
|
||||||
|
|||||||
@@ -43,46 +43,3 @@ export const checkTablesStatus = async (): Promise<{
|
|||||||
return tables;
|
return tables;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* 기존 테이블 목록을 가져옵니다.
|
|
||||||
* 참고: get_tables 함수는 사용하지 않음
|
|
||||||
*/
|
|
||||||
export const getExistingTables = async (): Promise<string[] | null> => {
|
|
||||||
try {
|
|
||||||
const tables = [];
|
|
||||||
|
|
||||||
// 직접 각 테이블 확인
|
|
||||||
const { error: transactionsError } = await supabase
|
|
||||||
.from('transactions')
|
|
||||||
.select('id')
|
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
if (!transactionsError) {
|
|
||||||
tables.push('transactions');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { error: budgetsError } = await supabase
|
|
||||||
.from('budgets')
|
|
||||||
.select('id')
|
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
if (!budgetsError) {
|
|
||||||
tables.push('budgets');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { error: categoryBudgetsError } = await supabase
|
|
||||||
.from('category_budgets')
|
|
||||||
.select('id')
|
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
if (!categoryBudgetsError) {
|
|
||||||
tables.push('category_budgets');
|
|
||||||
}
|
|
||||||
|
|
||||||
return tables;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('테이블 목록 확인 중 오류 발생:', error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
|
|
||||||
import { testAuth } from './authTests';
|
|
||||||
import { testRestApi } from './apiTests';
|
|
||||||
import { testDatabaseConnection } from './databaseTests';
|
|
||||||
import { TestResults, TestDebugInfo } from './types';
|
|
||||||
import { supabase, isValidUrl } from '../client';
|
|
||||||
import { getSupabaseUrl, getSupabaseKey, isCorsProxyEnabled, getProxyType } from '../config';
|
|
||||||
|
|
||||||
export const testSupabaseConnection = async (): Promise<TestResults> => {
|
|
||||||
// 기본 결과 객체 초기화
|
|
||||||
const results: TestResults = {
|
|
||||||
url: getSupabaseUrl(),
|
|
||||||
proxyUrl: '', // 빈 문자열로 초기화
|
|
||||||
usingProxy: isCorsProxyEnabled(),
|
|
||||||
proxyType: getProxyType(),
|
|
||||||
client: true,
|
|
||||||
restApi: false,
|
|
||||||
auth: false,
|
|
||||||
database: false,
|
|
||||||
errors: [],
|
|
||||||
debugInfo: {
|
|
||||||
originalUrl: getSupabaseUrl(),
|
|
||||||
proxyUrl: '',
|
|
||||||
usingProxy: isCorsProxyEnabled(),
|
|
||||||
proxyType: getProxyType(),
|
|
||||||
keyLength: getSupabaseKey().length,
|
|
||||||
browserInfo: navigator.userAgent,
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
backupProxySuccess: false,
|
|
||||||
lastErrorDetails: ''
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 클라이언트 유효성 체크
|
|
||||||
if (!supabase) {
|
|
||||||
results.client = false;
|
|
||||||
results.errors.push('Supabase 클라이언트 초기화 실패');
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
// CORS 프록시 URL 설정
|
|
||||||
if (results.usingProxy) {
|
|
||||||
const baseUrl = getSupabaseUrl();
|
|
||||||
const proxyType = getProxyType();
|
|
||||||
|
|
||||||
if (proxyType === 'corsproxy.io') {
|
|
||||||
results.proxyUrl = `https://corsproxy.io/?${encodeURIComponent(baseUrl)}`;
|
|
||||||
} else if (proxyType === 'cors-anywhere') {
|
|
||||||
results.proxyUrl = `https://cors-anywhere.herokuapp.com/${baseUrl}`;
|
|
||||||
} else {
|
|
||||||
results.proxyUrl = baseUrl; // 기본값
|
|
||||||
}
|
|
||||||
|
|
||||||
// debugInfo에도 proxyUrl 설정
|
|
||||||
results.debugInfo.proxyUrl = results.proxyUrl;
|
|
||||||
} else {
|
|
||||||
results.proxyUrl = results.url; // 프록시 사용 안 함
|
|
||||||
results.debugInfo.proxyUrl = results.url;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 테스트 실행
|
|
||||||
// testAuth 함수에 두 번째 인자로 URL 전달
|
|
||||||
const authResults = await testAuth(supabase, results.url);
|
|
||||||
const apiResults = await testRestApi(supabase);
|
|
||||||
const dbResults = await testDatabaseConnection(supabase);
|
|
||||||
|
|
||||||
// 결과 업데이트
|
|
||||||
results.auth = authResults.success;
|
|
||||||
results.restApi = apiResults.success;
|
|
||||||
results.database = dbResults.success;
|
|
||||||
|
|
||||||
// 오류 수집
|
|
||||||
if (!authResults.success && authResults.error) {
|
|
||||||
results.errors.push(`인증 테스트 실패: ${authResults.error}`);
|
|
||||||
results.debugInfo.lastErrorDetails += `인증: ${authResults.error}; `;
|
|
||||||
}
|
|
||||||
if (!apiResults.success && apiResults.error) {
|
|
||||||
results.errors.push(`REST API 테스트 실패: ${apiResults.error}`);
|
|
||||||
results.debugInfo.lastErrorDetails += `API: ${apiResults.error}; `;
|
|
||||||
}
|
|
||||||
if (!dbResults.success && dbResults.error) {
|
|
||||||
results.errors.push(`DB 테스트 실패: ${dbResults.error}`);
|
|
||||||
results.debugInfo.lastErrorDetails += `DB: ${dbResults.error}; `;
|
|
||||||
}
|
|
||||||
} catch (error: any) {
|
|
||||||
const errorMsg = `테스트 실행 오류: ${error.message || '알 수 없는 오류'}`;
|
|
||||||
results.errors.push(errorMsg);
|
|
||||||
results.debugInfo.lastErrorDetails = errorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
};
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
|
|
||||||
import React, { useState } from 'react';
|
|
||||||
import NavBar from '@/components/NavBar';
|
|
||||||
import SupabaseSettingsForm from '@/components/supabase/SupabaseSettingsForm';
|
|
||||||
import SupabaseConnectionTest from '@/components/supabase/SupabaseConnectionTest';
|
|
||||||
import SupabaseHelpSection from '@/components/supabase/SupabaseHelpSection';
|
|
||||||
|
|
||||||
const SupabaseSettings = () => {
|
|
||||||
const [refreshCounter, setRefreshCounter] = useState(0);
|
|
||||||
|
|
||||||
const handleSettingsSaved = () => {
|
|
||||||
// 설정이 저장될 때 테스트 컴포넌트를 다시 렌더링하기 위한 카운터 증가
|
|
||||||
setRefreshCounter(prev => prev + 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-neuro-background pb-24">
|
|
||||||
<div className="max-w-md mx-auto px-6">
|
|
||||||
<header className="py-8">
|
|
||||||
<h1 className="text-2xl font-bold neuro-text mb-5">Supabase 설정</h1>
|
|
||||||
<p className="text-gray-500 mb-8">
|
|
||||||
온프레미스 Supabase 인스턴스와 연결하기 위한 설정을 입력해주세요.
|
|
||||||
</p>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div className="neuro-card p-6 mb-6">
|
|
||||||
<SupabaseSettingsForm onSaved={handleSettingsSaved} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 연결 테스트 섹션 */}
|
|
||||||
<div className="neuro-card p-6 mb-6">
|
|
||||||
<SupabaseConnectionTest key={`test-${refreshCounter}`} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="neuro-card p-6">
|
|
||||||
<SupabaseHelpSection />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<NavBar />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SupabaseSettings;
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// 동기화 관련 모든 기능을 한 곳에서 내보내는 인덱스 파일
|
// 동기화 관련 모든 기능을 한 곳에서 내보내는 인덱스 파일
|
||||||
import { isSyncEnabled, setSyncEnabled, initSyncSettings } from './config';
|
import { isSyncEnabled, setSyncEnabled, initSyncSettings } from './config';
|
||||||
import { getLastSyncTime, setLastSyncTime } from './time';
|
import { getLastSyncTime, setLastSyncTime } from './time';
|
||||||
import { syncAllData as syncData } from './data'; // 이름 충돌 방지를 위해 이름 변경
|
import { syncAllData as syncData } from './data';
|
||||||
|
|
||||||
// 단일 진입점에서 모든 기능 내보내기
|
// 단일 진입점에서 모든 기능 내보내기
|
||||||
export {
|
export {
|
||||||
|
|||||||
Reference in New Issue
Block a user