Files
deals24togo_be/migrations/001_initial_schema.sql
belviskhoremk c4d836a0f9 Initial commit
2026-03-06 22:57:58 +00:00

181 lines
7.9 KiB
SQL

-- ============================================================
-- Deals24Togo — Supabase Database Schema
-- Run this in Supabase SQL Editor to create all tables
-- ============================================================
-- Enable UUID generation
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- ── USERS ────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
name TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'visitor'
CHECK (role IN ('admin', 'agency', 'visitor')),
verified BOOLEAN NOT NULL DEFAULT FALSE,
phone TEXT,
avatar_url TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_users_email ON users (email);
CREATE INDEX idx_users_role ON users (role);
-- ── AGENCIES ─────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS agencies (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
logo TEXT,
address TEXT NOT NULL DEFAULT '',
phone TEXT NOT NULL DEFAULT '',
email TEXT NOT NULL DEFAULT '',
website TEXT,
verified BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_agencies_user_id ON agencies (user_id);
CREATE INDEX idx_agencies_verified ON agencies (verified);
-- ── CATEGORIES ───────────────────────────────────────────
CREATE TABLE IF NOT EXISTS categories (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
icon TEXT NOT NULL DEFAULT 'tag',
slug TEXT UNIQUE NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_categories_slug ON categories (slug);
-- ── LISTINGS ─────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS listings (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
title TEXT NOT NULL,
description TEXT NOT NULL,
price NUMERIC(12, 2) NOT NULL DEFAULT 0,
images JSONB NOT NULL DEFAULT '[]'::JSONB,
status TEXT NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending', 'approved', 'rejected')),
agency_id UUID NOT NULL REFERENCES agencies(id) ON DELETE CASCADE,
category_id UUID NOT NULL REFERENCES categories(id) ON DELETE RESTRICT,
location TEXT NOT NULL DEFAULT '',
listing_type TEXT NOT NULL DEFAULT 'sale'
CHECK (listing_type IN ('sale', 'rent')),
condition TEXT CHECK (condition IN ('new', 'used', 'refurbished')),
negotiable BOOLEAN NOT NULL DEFAULT FALSE,
views_count INTEGER NOT NULL DEFAULT 0,
rejection_reason TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_listings_status ON listings (status);
CREATE INDEX idx_listings_agency_id ON listings (agency_id);
CREATE INDEX idx_listings_category_id ON listings (category_id);
CREATE INDEX idx_listings_price ON listings (price);
CREATE INDEX idx_listings_created_at ON listings (created_at DESC);
CREATE INDEX idx_listings_views_count ON listings (views_count DESC);
CREATE INDEX idx_listings_location ON listings USING gin (to_tsvector('english', location));
-- Full-text search index on title + description
CREATE INDEX idx_listings_fts ON listings USING gin (
to_tsvector('english', title || ' ' || description)
);
-- ── MESSAGES ─────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS messages (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
listing_id UUID NOT NULL REFERENCES listings(id) ON DELETE CASCADE,
agency_id UUID NOT NULL REFERENCES agencies(id) ON DELETE CASCADE,
name TEXT NOT NULL,
email TEXT NOT NULL,
phone TEXT,
message TEXT NOT NULL,
read BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_messages_agency_id ON messages (agency_id);
CREATE INDEX idx_messages_listing_id ON messages (listing_id);
CREATE INDEX idx_messages_read ON messages (read);
-- ── FAVORITES ────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS favorites (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
listing_id UUID NOT NULL REFERENCES listings(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (user_id, listing_id)
);
CREATE INDEX idx_favorites_user_id ON favorites (user_id);
CREATE INDEX idx_favorites_listing_id ON favorites (listing_id);
-- ── ROW LEVEL SECURITY (RLS) ────────────────────────────
-- NOTE: The backend uses the service_role key which bypasses RLS.
-- These policies are for any direct Supabase client access.
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
ALTER TABLE agencies ENABLE ROW LEVEL SECURITY;
ALTER TABLE categories ENABLE ROW LEVEL SECURITY;
ALTER TABLE listings ENABLE ROW LEVEL SECURITY;
ALTER TABLE messages ENABLE ROW LEVEL SECURITY;
ALTER TABLE favorites ENABLE ROW LEVEL SECURITY;
-- Public read for approved listings
CREATE POLICY "Public can view approved listings"
ON listings FOR SELECT
USING (status = 'approved');
-- Public read for categories
CREATE POLICY "Public can view categories"
ON categories FOR SELECT
TO anon, authenticated
USING (true);
-- Public read for verified agencies
CREATE POLICY "Public can view agencies"
ON agencies FOR SELECT
USING (true);
-- Users can read their own data
CREATE POLICY "Users can view own data"
ON users FOR SELECT
USING (auth.uid()::text = id::text);
-- Users can manage their own favorites
CREATE POLICY "Users manage own favorites"
ON favorites FOR ALL
USING (auth.uid()::text = user_id::text);
-- ── SEED DATA ────────────────────────────────────────────
-- Default categories
INSERT INTO categories (name, description, icon, slug) VALUES
('Real Estate', 'Homes, apartments, land, and commercial properties', 'home', 'real-estate'),
('Vehicles', 'Cars, motorcycles, boats, and other vehicles', 'car', 'vehicles'),
('Electronics', 'Computers, phones, TVs, and other electronic devices', 'smartphone', 'electronics'),
('Furniture', 'Home and office furniture, decor, and appliances', 'sofa', 'furniture'),
('Jobs', 'Job listings and career opportunities', 'briefcase', 'jobs'),
('Services', 'Professional services and skilled trades', 'wrench', 'services')
ON CONFLICT (slug) DO NOTHING;
-- ── STORAGE BUCKET ───────────────────────────────────────
-- Run this separately in Supabase Dashboard > Storage or via API:
-- CREATE BUCKET 'listings' with public access enabled