🗃️ db: supabase-schema.sql 멱등성 구조로 재작성
Some checks failed
ci / ci (22, ubuntu-latest) (push) Failing after 19m1s
Some checks failed
ci / ci (22, ubuntu-latest) (push) Failing after 19m1s
- ENUM 타입: CREATE TYPE → DO $$ ... EXCEPTION WHEN duplicate_object 블록 - 테이블: CREATE TABLE → CREATE TABLE IF NOT EXISTS - 트리거: DROP TRIGGER IF EXISTS 후 재생성 - RLS 정책: DROP POLICY IF EXISTS 후 재생성 - purchases.quantity 컬럼 추가: ALTER TABLE ... ADD COLUMN IF NOT EXISTS - 전체 SQL을 재실행해도 오류 없이 동작
This commit is contained in:
@@ -1,70 +1,101 @@
|
||||
-- ============================================================
|
||||
-- CampGear Supabase Schema
|
||||
-- Run this in Supabase SQL Editor
|
||||
-- Supabase SQL Editor에서 실행
|
||||
-- 멱등성 보장: 재실행해도 안전 (IF NOT EXISTS / DO 블록 사용)
|
||||
-- ============================================================
|
||||
|
||||
-- Enable UUID extension
|
||||
-- UUID 확장
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
-- ENUM types
|
||||
CREATE TYPE equipment_category AS ENUM (
|
||||
'tent', 'sleeping', 'cooking', 'lighting', 'clothing',
|
||||
'backpack', 'furniture', 'safety', 'electronics', 'other'
|
||||
);
|
||||
|
||||
CREATE TYPE sale_status AS ENUM ('listing', 'reserved', 'sold', 'cancelled');
|
||||
CREATE TYPE sale_platform AS ENUM ('danggeun', 'bunjang', 'joonggo', 'naver', 'other');
|
||||
-- ============================================================
|
||||
-- ENUM 타입 (이미 존재하면 무시)
|
||||
-- ============================================================
|
||||
|
||||
-- purchases table
|
||||
CREATE TABLE purchases (
|
||||
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
|
||||
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
category equipment_category NOT NULL,
|
||||
brand TEXT,
|
||||
price INTEGER NOT NULL DEFAULT 0,
|
||||
purchase_date DATE NOT NULL,
|
||||
store TEXT,
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE equipment_category AS ENUM (
|
||||
'tent', 'sleeping', 'cooking', 'lighting', 'clothing',
|
||||
'backpack', 'furniture', 'safety', 'electronics', 'other'
|
||||
);
|
||||
EXCEPTION WHEN duplicate_object THEN NULL;
|
||||
END $$;
|
||||
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE sale_status AS ENUM ('listing', 'reserved', 'sold', 'cancelled');
|
||||
EXCEPTION WHEN duplicate_object THEN NULL;
|
||||
END $$;
|
||||
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE sale_platform AS ENUM ('danggeun', 'bunjang', 'joonggo', 'naver', 'other');
|
||||
EXCEPTION WHEN duplicate_object THEN NULL;
|
||||
END $$;
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- 테이블
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS purchases (
|
||||
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
|
||||
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
category equipment_category NOT NULL,
|
||||
brand TEXT,
|
||||
price INTEGER NOT NULL DEFAULT 0,
|
||||
quantity INTEGER NOT NULL DEFAULT 1,
|
||||
purchase_date DATE NOT NULL,
|
||||
store TEXT,
|
||||
warranty_until DATE,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
|
||||
);
|
||||
|
||||
-- used_sales table
|
||||
CREATE TABLE used_sales (
|
||||
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
|
||||
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,
|
||||
purchase_id UUID REFERENCES purchases(id) ON DELETE SET NULL,
|
||||
item_name TEXT NOT NULL,
|
||||
sale_price INTEGER NOT NULL DEFAULT 0,
|
||||
CREATE TABLE IF NOT EXISTS used_sales (
|
||||
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
|
||||
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,
|
||||
purchase_id UUID REFERENCES purchases(id) ON DELETE SET NULL,
|
||||
item_name TEXT NOT NULL,
|
||||
sale_price INTEGER NOT NULL DEFAULT 0,
|
||||
final_price INTEGER,
|
||||
platform sale_platform NOT NULL,
|
||||
status sale_status NOT NULL DEFAULT 'listing',
|
||||
notes TEXT,
|
||||
listed_at DATE NOT NULL,
|
||||
sold_at DATE,
|
||||
platform sale_platform NOT NULL,
|
||||
status sale_status NOT NULL DEFAULT 'listing',
|
||||
notes TEXT,
|
||||
listed_at DATE NOT NULL,
|
||||
sold_at DATE,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ai_conversations (
|
||||
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
|
||||
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,
|
||||
title TEXT NOT NULL DEFAULT '새 대화',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
|
||||
);
|
||||
|
||||
-- ai_conversations table
|
||||
CREATE TABLE ai_conversations (
|
||||
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
|
||||
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,
|
||||
title TEXT NOT NULL DEFAULT '새 대화',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
|
||||
CREATE TABLE IF NOT EXISTS ai_messages (
|
||||
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
|
||||
conversation_id UUID REFERENCES ai_conversations(id) ON DELETE CASCADE NOT NULL,
|
||||
role TEXT NOT NULL CHECK (role IN ('user', 'assistant')),
|
||||
content TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
|
||||
);
|
||||
|
||||
-- ai_messages table
|
||||
CREATE TABLE ai_messages (
|
||||
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
|
||||
conversation_id UUID REFERENCES ai_conversations(id) ON DELETE CASCADE NOT NULL,
|
||||
role TEXT NOT NULL CHECK (role IN ('user', 'assistant')),
|
||||
content TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
|
||||
);
|
||||
|
||||
-- updated_at trigger function
|
||||
-- ============================================================
|
||||
-- 컬럼 마이그레이션 (ADD COLUMN IF NOT EXISTS)
|
||||
-- ============================================================
|
||||
|
||||
-- purchases.quantity 추가 (기존 테이블에 없을 경우)
|
||||
ALTER TABLE purchases ADD COLUMN IF NOT EXISTS quantity INTEGER NOT NULL DEFAULT 1;
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- updated_at 자동 갱신 트리거
|
||||
-- ============================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION update_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
@@ -73,35 +104,48 @@ BEGIN
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Apply triggers
|
||||
CREATE TRIGGER purchases_updated_at BEFORE UPDATE ON purchases
|
||||
DROP TRIGGER IF EXISTS purchases_updated_at ON purchases;
|
||||
CREATE TRIGGER purchases_updated_at
|
||||
BEFORE UPDATE ON purchases
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
|
||||
CREATE TRIGGER used_sales_updated_at BEFORE UPDATE ON used_sales
|
||||
DROP TRIGGER IF EXISTS used_sales_updated_at ON used_sales;
|
||||
CREATE TRIGGER used_sales_updated_at
|
||||
BEFORE UPDATE ON used_sales
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
|
||||
CREATE TRIGGER ai_conversations_updated_at BEFORE UPDATE ON ai_conversations
|
||||
DROP TRIGGER IF EXISTS ai_conversations_updated_at ON ai_conversations;
|
||||
CREATE TRIGGER ai_conversations_updated_at
|
||||
BEFORE UPDATE ON ai_conversations
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- Row Level Security
|
||||
ALTER TABLE purchases ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE used_sales ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE ai_conversations ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE ai_messages ENABLE ROW LEVEL SECURITY;
|
||||
-- ============================================================
|
||||
|
||||
-- RLS Policies: purchases
|
||||
ALTER TABLE purchases ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE used_sales ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE ai_conversations ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE ai_messages ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- purchases RLS
|
||||
DROP POLICY IF EXISTS "Users can manage own purchases" ON purchases;
|
||||
CREATE POLICY "Users can manage own purchases" ON purchases
|
||||
FOR ALL USING (auth.uid() = user_id);
|
||||
|
||||
-- RLS Policies: used_sales
|
||||
-- used_sales RLS
|
||||
DROP POLICY IF EXISTS "Users can manage own used_sales" ON used_sales;
|
||||
CREATE POLICY "Users can manage own used_sales" ON used_sales
|
||||
FOR ALL USING (auth.uid() = user_id);
|
||||
|
||||
-- RLS Policies: ai_conversations
|
||||
-- ai_conversations RLS
|
||||
DROP POLICY IF EXISTS "Users can manage own conversations" ON ai_conversations;
|
||||
CREATE POLICY "Users can manage own conversations" ON ai_conversations
|
||||
FOR ALL USING (auth.uid() = user_id);
|
||||
|
||||
-- RLS Policies: ai_messages (via conversation ownership)
|
||||
-- ai_messages RLS (대화 소유자를 통해 접근)
|
||||
DROP POLICY IF EXISTS "Users can manage messages in own conversations" ON ai_messages;
|
||||
CREATE POLICY "Users can manage messages in own conversations" ON ai_messages
|
||||
FOR ALL USING (
|
||||
conversation_id IN (
|
||||
|
||||
Reference in New Issue
Block a user