Investigate table creation API

Explore the possibility of creating tables directly via the Supabase API.
This commit is contained in:
gpt-engineer-app[bot]
2025-03-15 13:05:45 +00:00
parent 4bacba4719
commit a8537d5398
3 changed files with 254 additions and 5 deletions

View File

@@ -4,7 +4,7 @@ import { Link } from "react-router-dom";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { ArrowRight, Mail, KeyRound, Eye, EyeOff, AlertTriangle } from "lucide-react";
import { ArrowRight, Mail, KeyRound, Eye, EyeOff, AlertTriangle, Loader2 } from "lucide-react";
interface LoginFormProps {
email: string;
@@ -14,6 +14,7 @@ interface LoginFormProps {
showPassword: boolean;
setShowPassword: (show: boolean) => void;
isLoading: boolean;
isSettingUpTables?: boolean;
loginError: string | null;
handleLogin: (e: React.FormEvent) => Promise<void>;
}
@@ -26,6 +27,7 @@ const LoginForm: React.FC<LoginFormProps> = ({
showPassword,
setShowPassword,
isLoading,
isSettingUpTables = false,
loginError,
handleLogin
}) => {
@@ -102,11 +104,14 @@ const LoginForm: React.FC<LoginFormProps> = ({
<Button
type="submit"
disabled={isLoading}
disabled={isLoading || isSettingUpTables}
className="w-full hover:bg-neuro-income/80 text-white py-6 h-auto bg-neuro-income"
>
{isLoading ? "로그인 중..." : "로그인"}
{!isLoading && <ArrowRight className="ml-2 h-5 w-5" />}
{isLoading ? "로그인 중..." :
isSettingUpTables ? "데이터베이스 설정 중..." : "로그인"}
{!isLoading && !isSettingUpTables ?
<ArrowRight className="ml-2 h-5 w-5" /> :
<Loader2 className="ml-2 h-5 w-5 animate-spin" />}
</Button>
</div>
</form>

View File

@@ -3,6 +3,7 @@ import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { useToast } from "@/hooks/useToast.wrapper";
import { useAuth } from "@/contexts/auth";
import { createRequiredTables } from "@/lib/supabase/setup";
export function useLogin() {
const [email, setEmail] = useState("");
@@ -10,6 +11,7 @@ export function useLogin() {
const [showPassword, setShowPassword] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [loginError, setLoginError] = useState<string | null>(null);
const [isSettingUpTables, setIsSettingUpTables] = useState(false);
const navigate = useNavigate();
const { toast } = useToast();
const { signIn } = useAuth();
@@ -30,7 +32,7 @@ export function useLogin() {
setIsLoading(true);
try {
const { error } = await signIn(email, password);
const { error, user } = await signIn(email, password);
if (error) {
console.error("로그인 실패:", error);
@@ -61,6 +63,23 @@ export function useLogin() {
variant: "destructive"
});
} else {
// 로그인 성공 시 필요한 테이블 설정
try {
setIsSettingUpTables(true);
const { success, message } = await createRequiredTables();
if (success) {
console.log("테이블 설정 성공:", message);
} else {
console.warn("테이블 설정 문제:", message);
// 테이블 설정 실패해도 로그인은 진행
}
} catch (setupError) {
console.error("테이블 설정 중 오류:", setupError);
} finally {
setIsSettingUpTables(false);
}
navigate("/");
}
} catch (err: any) {
@@ -86,6 +105,7 @@ export function useLogin() {
showPassword,
setShowPassword,
isLoading,
isSettingUpTables,
loginError,
setLoginError,
handleLogin

224
src/lib/supabase/setup.ts Normal file
View File

@@ -0,0 +1,224 @@
import { supabase } from './client';
/**
* Supabase 데이터베이스에 필요한 테이블을 생성합니다.
* 이 함수는 로그인 후 실행되어야 합니다.
*/
export const createRequiredTables = async (): Promise<{ success: boolean; message: string }> => {
try {
console.log('데이터베이스 테이블 생성 시작...');
// 테이블이 이미 존재하는지 확인
const { data: existingTables, error: tableError } = await supabase
.rpc('get_tables');
if (tableError) {
console.error('테이블 목록 가져오기 실패:', tableError);
// RPC가 존재하지 않는 경우, 다른 방법으로 테이블 생성 시도
return await createTablesWithRawSQL();
}
console.log('기존 테이블:', existingTables);
// 이미 테이블이 존재하는 경우
if (Array.isArray(existingTables) &&
existingTables.some(t => t.name === 'transactions') &&
existingTables.some(t => t.name === 'budgets')) {
return {
success: true,
message: '필요한 테이블이 이미 존재합니다.'
};
}
return await createTablesWithRawSQL();
} catch (error: any) {
console.error('테이블 생성 중 오류 발생:', error);
return {
success: false,
message: `테이블 생성 실패: ${error.message || '알 수 없는 오류'}`
};
}
};
/**
* SQL 쿼리를 직접 실행하여 테이블을 생성합니다.
*/
const createTablesWithRawSQL = async (): Promise<{ success: boolean; message: string }> => {
try {
// transactions 테이블 생성
const { error: transactionsError } = await supabase.rpc('execute_sql', {
sql_query: `
CREATE TABLE IF NOT EXISTS transactions (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES auth.users(id) NOT NULL,
title TEXT NOT NULL,
amount NUMERIC NOT NULL,
category TEXT NOT NULL,
date TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
type TEXT NOT NULL,
notes TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Row Level Security 설정
ALTER TABLE transactions ENABLE ROW LEVEL SECURITY;
-- 사용자 정책 설정 (읽기)
CREATE POLICY "사용자는 자신의 트랜잭션만 볼 수 있음"
ON transactions FOR SELECT
USING (auth.uid() = user_id);
-- 사용자 정책 설정 (쓰기)
CREATE POLICY "사용자는 자신의 트랜잭션만 추가할 수 있음"
ON transactions FOR INSERT
WITH CHECK (auth.uid() = user_id);
-- 사용자 정책 설정 (업데이트)
CREATE POLICY "사용자는 자신의 트랜잭션만 업데이트할 수 있음"
ON transactions FOR UPDATE
USING (auth.uid() = user_id);
-- 사용자 정책 설정 (삭제)
CREATE POLICY "사용자는 자신의 트랜잭션만 삭제할 수 있음"
ON transactions FOR DELETE
USING (auth.uid() = user_id);
`
});
if (transactionsError) {
console.error('transactions 테이블 생성 실패:', transactionsError);
return {
success: false,
message: `transactions 테이블 생성 실패: ${transactionsError.message}`
};
}
// budgets 테이블 생성
const { error: budgetsError } = await supabase.rpc('execute_sql', {
sql_query: `
CREATE TABLE IF NOT EXISTS budgets (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES auth.users(id) NOT NULL,
month INTEGER NOT NULL,
year INTEGER NOT NULL,
total_budget NUMERIC NOT NULL DEFAULT 0,
categories JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE (user_id, month, year)
);
-- Row Level Security 설정
ALTER TABLE budgets ENABLE ROW LEVEL SECURITY;
-- 사용자 정책 설정 (읽기)
CREATE POLICY "사용자는 자신의 예산만 볼 수 있음"
ON budgets FOR SELECT
USING (auth.uid() = user_id);
-- 사용자 정책 설정 (쓰기)
CREATE POLICY "사용자는 자신의 예산만 추가할 수 있음"
ON budgets FOR INSERT
WITH CHECK (auth.uid() = user_id);
-- 사용자 정책 설정 (업데이트)
CREATE POLICY "사용자는 자신의 예산만 업데이트할 수 있음"
ON budgets FOR UPDATE
USING (auth.uid() = user_id);
-- 사용자 정책 설정 (삭제)
CREATE POLICY "사용자는 자신의 예산만 삭제할 수 있음"
ON budgets FOR DELETE
USING (auth.uid() = user_id);
`
});
if (budgetsError) {
console.error('budgets 테이블 생성 실패:', budgetsError);
return {
success: false,
message: `budgets 테이블 생성 실패: ${budgetsError.message}`
};
}
// _tests 테이블 생성 (연결 테스트용)
const { error: testsError } = await supabase.rpc('execute_sql', {
sql_query: `
CREATE TABLE IF NOT EXISTS _tests (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
test_name TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- 모든 사용자가 접근 가능하도록 설정
ALTER TABLE _tests ENABLE ROW LEVEL SECURITY;
CREATE POLICY "모든 사용자가 테스트 테이블에 접근 가능"
ON _tests FOR SELECT
USING (true);
`
});
if (testsError) {
console.error('_tests 테이블 생성 실패:', testsError);
// _tests 테이블은 필수가 아니므로 오류가 발생해도 진행
}
return {
success: true,
message: '모든 필수 테이블이 성공적으로 생성되었습니다.'
};
} catch (error: any) {
console.error('테이블 생성 중 오류 발생:', error);
return {
success: false,
message: `테이블 생성 실패: ${error.message || '알 수 없는 오류'}`
};
}
};
/**
* 테이블 상태를 확인합니다.
*/
export const checkTablesStatus = async (): Promise<{
transactions: boolean;
budgets: boolean;
tests: boolean;
}> => {
const tables = {
transactions: false,
budgets: false,
tests: false
};
try {
// transactions 테이블 확인
const { count: transactionsCount, error: transactionsError } = await supabase
.from('transactions')
.select('*', { count: 'exact', head: true });
tables.transactions = !transactionsError;
// budgets 테이블 확인
const { count: budgetsCount, error: budgetsError } = await supabase
.from('budgets')
.select('*', { count: 'exact', head: true });
tables.budgets = !budgetsError;
// _tests 테이블 확인
const { count: testsCount, error: testsError } = await supabase
.from('_tests')
.select('*', { count: 'exact', head: true });
tables.tests = !testsError;
return tables;
} catch (error) {
console.error('테이블 상태 확인 중 오류 발생:', error);
return tables;
}
};