🌸 Danchoiluxury.com - Booking PGA & SGBB Uy tín | Bảo Mật | Lớn Nhất Việt Nam

✓ ĐÃ XÁC MINH

Cộng đồng chia sẻ hồ sơ PGA: người mẫu, sinh viên ,hoa khôi ,nhân viên văn phòng, nhân viên ngân hàng , tiếp viên hàng không,hoa hậu, á hậu...uy tín toàn quốc. Tất cả hồ sơ đều được xác minh thực tế — hoàn tiền nếu phát hiện tráo hàng, bom khách. Dịch vụ phục vụ tận nơi, hỗ trợ 24/7, bảo mật thông tin khách hàng tuyệt đối.

Supabase Schema

Copy toàn bộ SQL này và chạy trong Supabase → SQL Editor

1

Copy SQL

Nhấn nút Copy bên dưới

2

Supabase SQL Editor

Mở project → SQL Editor → New query

3

Run

Paste vào và nhấn Run

profiles

  • Mọi người xem được
  • Chỉ chủ sở hữu/admin sửa
  • Chỉ admin xóa

models

  • Mọi người xem available
  • Chỉ admin thêm/sửa/xóa

bookings

  • User chỉ xem booking của mình
  • Admin xem tất cả + duyệt đơn

comments

  • Mọi người xem được
  • Phải đăng nhập để bình luận
  • Chỉ chủ comment/admin sửa xóa

site_settings

  • Mọi người xem được (cần cho SEO)
  • Chỉ admin thêm/sửa/xóa
supabase_schema.sql
-- ============================================================
-- ModelVN – Supabase Schema + RLS Policies
-- Chạy toàn bộ script này trong Supabase SQL Editor
-- ============================================================

-- Enable UUID extension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

-- ============================================================
-- TABLE: profiles (extends auth.users)
-- ============================================================
CREATE TABLE IF NOT EXISTS public.profiles (
  id UUID REFERENCES auth.users(id) ON DELETE CASCADE PRIMARY KEY,
  full_name TEXT,
  email TEXT,
  avatar TEXT,
  phone TEXT,
  role TEXT NOT NULL DEFAULT 'user' CHECK (role IN ('admin', 'user')),
  vip_level TEXT DEFAULT 'bronze' CHECK (vip_level IN ('bronze', 'silver', 'gold', 'platinum', 'diamond', 'legend')),
  vip_expires_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Auto-create profile on signup
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
  INSERT INTO public.profiles (id, full_name, email)
  VALUES (NEW.id, NEW.raw_user_meta_data->>'full_name', NEW.email);
  RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

CREATE OR REPLACE TRIGGER on_auth_user_created
  AFTER INSERT ON auth.users
  FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();

-- ============================================================
-- TABLE: models
-- ============================================================
CREATE TABLE IF NOT EXISTS public.models (
  id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
  name TEXT NOT NULL,
  stage_name TEXT,
  avatar TEXT,
  gallery TEXT[] DEFAULT '{}',
  region TEXT NOT NULL CHECK (region IN ('mien_bac', 'mien_trung', 'mien_nam')),
  city TEXT,
  height NUMERIC,
  weight NUMERIC,
  measurements TEXT,
  booking_price NUMERIC NOT NULL DEFAULT 0,
  booking_price_day NUMERIC,
  categories TEXT[] DEFAULT '{}',
  experience TEXT,
  bio TEXT,
  is_featured BOOLEAN DEFAULT FALSE,
  is_verified BOOLEAN DEFAULT FALSE,
  rating NUMERIC DEFAULT 0,
  total_bookings INTEGER DEFAULT 0,
  status TEXT DEFAULT 'available' CHECK (status IN ('available', 'busy', 'inactive')),
  created_by UUID REFERENCES auth.users(id),
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- ============================================================
-- TABLE: bookings
-- ============================================================
CREATE TABLE IF NOT EXISTS public.bookings (
  id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
  model_id UUID REFERENCES public.models(id) ON DELETE SET NULL,
  model_name TEXT,
  event_type TEXT NOT NULL,
  event_date DATE NOT NULL,
  duration_hours NUMERIC,
  location TEXT,
  note TEXT,
  status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'confirmed', 'cancelled')),
  total_price NUMERIC,
  created_by UUID REFERENCES auth.users(id),
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- ============================================================
-- TABLE: comments
-- ============================================================
CREATE TABLE IF NOT EXISTS public.comments (
  id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
  model_id UUID REFERENCES public.models(id) ON DELETE CASCADE NOT NULL,
  content TEXT NOT NULL,
  rating NUMERIC CHECK (rating >= 1 AND rating <= 5),
  parent_id UUID REFERENCES public.comments(id) ON DELETE CASCADE,
  likes INTEGER DEFAULT 0,
  liked_by TEXT[] DEFAULT '{}',
  created_by UUID REFERENCES auth.users(id),
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- ============================================================
-- TABLE: site_settings
-- ============================================================
CREATE TABLE IF NOT EXISTS public.site_settings (
  id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
  key TEXT UNIQUE NOT NULL,
  value TEXT,
  group_name TEXT CHECK (group_name IN ('seo', 'general', 'contact', 'social')),
  label TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- ============================================================
-- INDEXES
-- ============================================================
CREATE INDEX IF NOT EXISTS idx_models_region ON public.models(region);
CREATE INDEX IF NOT EXISTS idx_models_status ON public.models(status);
CREATE INDEX IF NOT EXISTS idx_models_is_featured ON public.models(is_featured);
CREATE INDEX IF NOT EXISTS idx_bookings_created_by ON public.bookings(created_by);
CREATE INDEX IF NOT EXISTS idx_bookings_model_id ON public.bookings(model_id);
CREATE INDEX IF NOT EXISTS idx_bookings_status ON public.bookings(status);
CREATE INDEX IF NOT EXISTS idx_comments_model_id ON public.comments(model_id);
CREATE INDEX IF NOT EXISTS idx_site_settings_key ON public.site_settings(key);

-- ============================================================
-- ROW LEVEL SECURITY (RLS)
-- ============================================================

ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.models ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.bookings ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.comments ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.site_settings ENABLE ROW LEVEL SECURITY;

-- Helper: check if current user is admin
CREATE OR REPLACE FUNCTION public.is_admin()
RETURNS BOOLEAN AS $$
  SELECT EXISTS (
    SELECT 1 FROM public.profiles
    WHERE id = auth.uid() AND role = 'admin'
  );
$$ LANGUAGE sql SECURITY DEFINER STABLE;

-- ============================================================
-- RLS: profiles
-- ============================================================
-- Mọi người có thể xem profile công khai
CREATE POLICY "profiles_select_all" ON public.profiles
  FOR SELECT USING (true);

-- Chỉ chủ sở hữu hoặc admin mới được update
CREATE POLICY "profiles_update_own" ON public.profiles
  FOR UPDATE USING (auth.uid() = id OR public.is_admin());

-- Chỉ admin mới được xóa
CREATE POLICY "profiles_delete_admin" ON public.profiles
  FOR DELETE USING (public.is_admin());

-- ============================================================
-- RLS: models
-- ============================================================
-- Mọi người (kể cả anonymous) có thể xem model available
CREATE POLICY "models_select_public" ON public.models
  FOR SELECT USING (status != 'inactive' OR public.is_admin());

-- Chỉ admin mới được thêm model
CREATE POLICY "models_insert_admin" ON public.models
  FOR INSERT WITH CHECK (public.is_admin());

-- Chỉ admin mới được sửa model
CREATE POLICY "models_update_admin" ON public.models
  FOR UPDATE USING (public.is_admin());

-- Chỉ admin mới được xóa model
CREATE POLICY "models_delete_admin" ON public.models
  FOR DELETE USING (public.is_admin());

-- ============================================================
-- RLS: bookings
-- ============================================================
-- User chỉ xem booking của mình, admin xem tất cả
CREATE POLICY "bookings_select" ON public.bookings
  FOR SELECT USING (auth.uid() = created_by OR public.is_admin());

-- User đã đăng nhập mới được tạo booking
CREATE POLICY "bookings_insert_auth" ON public.bookings
  FOR INSERT WITH CHECK (auth.uid() IS NOT NULL AND auth.uid() = created_by);

-- Chỉ admin mới được cập nhật trạng thái booking
CREATE POLICY "bookings_update_admin" ON public.bookings
  FOR UPDATE USING (public.is_admin());

-- Chỉ admin hoặc chủ booking mới được hủy
CREATE POLICY "bookings_delete" ON public.bookings
  FOR DELETE USING (auth.uid() = created_by OR public.is_admin());

-- ============================================================
-- RLS: comments
-- ============================================================
-- Mọi người đều xem được comment
CREATE POLICY "comments_select_all" ON public.comments
  FOR SELECT USING (true);

-- User đã đăng nhập mới được bình luận
CREATE POLICY "comments_insert_auth" ON public.comments
  FOR INSERT WITH CHECK (auth.uid() IS NOT NULL AND auth.uid() = created_by);

-- Chỉ chủ comment hoặc admin mới được sửa
CREATE POLICY "comments_update_own" ON public.comments
  FOR UPDATE USING (auth.uid() = created_by OR public.is_admin());

-- Chỉ chủ comment hoặc admin mới được xóa
CREATE POLICY "comments_delete_own" ON public.comments
  FOR DELETE USING (auth.uid() = created_by OR public.is_admin());

-- ============================================================
-- RLS: site_settings
-- ============================================================
-- Mọi người xem được settings (cần cho SEO meta tags)
CREATE POLICY "settings_select_all" ON public.site_settings
  FOR SELECT USING (true);

-- Chỉ admin mới được thêm/sửa/xóa settings
CREATE POLICY "settings_insert_admin" ON public.site_settings
  FOR INSERT WITH CHECK (public.is_admin());

CREATE POLICY "settings_update_admin" ON public.site_settings
  FOR UPDATE USING (public.is_admin());

CREATE POLICY "settings_delete_admin" ON public.site_settings
  FOR DELETE USING (public.is_admin());

-- ============================================================
-- AUTO-UPDATE updated_at trigger
-- ============================================================
CREATE OR REPLACE FUNCTION public.set_updated_at()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = NOW();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER set_models_updated_at BEFORE UPDATE ON public.models FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
CREATE TRIGGER set_bookings_updated_at BEFORE UPDATE ON public.bookings FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
CREATE TRIGGER set_comments_updated_at BEFORE UPDATE ON public.comments FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
CREATE TRIGGER set_settings_updated_at BEFORE UPDATE ON public.site_settings FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
CREATE TRIGGER set_profiles_updated_at BEFORE UPDATE ON public.profiles FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();

-- ============================================================
-- Grant quyền cho anon và authenticated roles
-- ============================================================
GRANT USAGE ON SCHEMA public TO anon, authenticated;
GRANT SELECT ON public.models TO anon;
GRANT SELECT ON public.comments TO anon;
GRANT SELECT ON public.site_settings TO anon;
GRANT ALL ON public.profiles TO authenticated;
GRANT ALL ON public.models TO authenticated;
GRANT ALL ON public.bookings TO authenticated;
GRANT ALL ON public.comments TO authenticated;
GRANT ALL ON public.site_settings TO authenticated;

-- ============================================================
-- DONE! Chạy xong là Supabase đã sẵn sàng cho ModelVN
-- ============================================================