feat: update database schema and migrations

This commit is contained in:
eligrinfeld
2025-01-06 21:24:54 -07:00
parent 765c8e549c
commit 7fa0e9dd9d
9 changed files with 635 additions and 2 deletions

View File

@ -0,0 +1,75 @@
-- Create businesses table
CREATE TABLE IF NOT EXISTS public.businesses (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
name TEXT NOT NULL,
address TEXT NOT NULL,
phone TEXT NOT NULL,
description TEXT NOT NULL,
website TEXT,
source TEXT NOT NULL,
rating REAL,
location POINT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Create indexes
CREATE INDEX IF NOT EXISTS idx_businesses_source ON public.businesses(source);
CREATE INDEX IF NOT EXISTS idx_businesses_rating ON public.businesses(rating);
CREATE INDEX IF NOT EXISTS idx_businesses_location ON public.businesses USING GIST(location);
-- Enable Row Level Security (RLS)
ALTER TABLE public.businesses ENABLE ROW LEVEL SECURITY;
-- Create policies
CREATE POLICY "Allow public read access"
ON public.businesses
FOR SELECT
USING (true);
CREATE POLICY "Allow service role insert/update"
ON public.businesses
FOR ALL
USING (auth.role() = 'service_role');
-- Create function to update updated_at timestamp
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ language 'plpgsql';
-- Create trigger for updated_at
CREATE TRIGGER update_businesses_updated_at
BEFORE UPDATE ON public.businesses
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
-- Create the searches table
CREATE TABLE searches (
id BIGSERIAL PRIMARY KEY,
query TEXT NOT NULL,
results JSONB NOT NULL DEFAULT '[]',
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- Create an index on the query column for faster lookups
CREATE INDEX searches_query_idx ON searches USING GIN (to_tsvector('english', query));
-- Create a function to update the updated_at timestamp
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ language 'plpgsql';
-- Create a trigger to automatically update the updated_at column
CREATE TRIGGER update_searches_updated_at
BEFORE UPDATE ON searches
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();

View File

@ -0,0 +1,63 @@
-- Create business_profiles table
CREATE TABLE IF NOT EXISTS public.business_profiles (
business_id TEXT PRIMARY KEY REFERENCES public.businesses(id),
claimed_by UUID REFERENCES auth.users(id),
claimed_at TIMESTAMP WITH TIME ZONE,
verification_status TEXT NOT NULL DEFAULT 'unverified',
social_links JSONB DEFAULT '{}',
hours_of_operation JSONB DEFAULT '{}',
additional_photos TEXT[] DEFAULT '{}',
tags TEXT[] DEFAULT '{}',
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT valid_verification_status CHECK (verification_status IN ('unverified', 'pending', 'verified', 'rejected'))
);
-- Create business_claims table to track claim requests
CREATE TABLE IF NOT EXISTS public.business_claims (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
business_id TEXT NOT NULL REFERENCES public.businesses(id),
user_id UUID NOT NULL REFERENCES auth.users(id),
status TEXT NOT NULL DEFAULT 'pending',
proof_documents TEXT[] DEFAULT '{}',
submitted_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
reviewed_at TIMESTAMP WITH TIME ZONE,
reviewed_by UUID REFERENCES auth.users(id),
notes TEXT,
CONSTRAINT valid_claim_status CHECK (status IN ('pending', 'approved', 'rejected'))
);
-- Create indexes
CREATE INDEX IF NOT EXISTS idx_business_profiles_claimed_by ON public.business_profiles(claimed_by);
CREATE INDEX IF NOT EXISTS idx_business_claims_business_id ON public.business_claims(business_id);
CREATE INDEX IF NOT EXISTS idx_business_claims_user_id ON public.business_claims(user_id);
CREATE INDEX IF NOT EXISTS idx_business_claims_status ON public.business_claims(status);
-- Add RLS policies
ALTER TABLE public.business_profiles ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.business_claims ENABLE ROW LEVEL SECURITY;
-- Policies for business_profiles
CREATE POLICY "Public profiles are viewable by everyone"
ON public.business_profiles FOR SELECT
USING (true);
CREATE POLICY "Profiles can be updated by verified owners"
ON public.business_profiles FOR UPDATE
USING (auth.uid() = claimed_by AND verification_status = 'verified');
-- Policies for business_claims
CREATE POLICY "Users can view their own claims"
ON public.business_claims FOR SELECT
USING (auth.uid() = user_id);
CREATE POLICY "Users can create claims"
ON public.business_claims FOR INSERT
WITH CHECK (auth.uid() = user_id);
CREATE POLICY "Only admins can review claims"
ON public.business_claims FOR UPDATE
USING (EXISTS (
SELECT 1 FROM auth.users
WHERE auth.uid() = id
AND raw_app_meta_data->>'role' = 'admin'
));

View File

@ -0,0 +1,98 @@
-- Function to create businesses table
CREATE OR REPLACE FUNCTION create_businesses_table()
RETURNS void AS $$
BEGIN
CREATE TABLE IF NOT EXISTS public.businesses (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
phone TEXT,
email TEXT,
address TEXT,
rating NUMERIC,
website TEXT,
description TEXT,
source TEXT,
logo TEXT,
latitude NUMERIC,
longitude NUMERIC,
last_updated TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
search_count INTEGER DEFAULT 1,
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
place_id TEXT
);
END;
$$ LANGUAGE plpgsql;
-- Function to create business_profiles table
CREATE OR REPLACE FUNCTION create_business_profiles_table()
RETURNS void AS $$
BEGIN
CREATE TABLE IF NOT EXISTS public.business_profiles (
business_id TEXT PRIMARY KEY REFERENCES public.businesses(id),
claimed_by UUID REFERENCES auth.users(id),
claimed_at TIMESTAMP WITH TIME ZONE,
verification_status TEXT NOT NULL DEFAULT 'unverified',
social_links JSONB DEFAULT '{}',
hours_of_operation JSONB DEFAULT '{}',
additional_photos TEXT[] DEFAULT '{}',
tags TEXT[] DEFAULT '{}',
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT valid_verification_status CHECK (verification_status IN ('unverified', 'pending', 'verified', 'rejected'))
);
-- Create business_claims table
CREATE TABLE IF NOT EXISTS public.business_claims (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
business_id TEXT NOT NULL REFERENCES public.businesses(id),
user_id UUID NOT NULL REFERENCES auth.users(id),
status TEXT NOT NULL DEFAULT 'pending',
proof_documents TEXT[] DEFAULT '{}',
submitted_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
reviewed_at TIMESTAMP WITH TIME ZONE,
reviewed_by UUID REFERENCES auth.users(id),
notes TEXT,
CONSTRAINT valid_claim_status CHECK (status IN ('pending', 'approved', 'rejected'))
);
-- Create indexes
CREATE INDEX IF NOT EXISTS idx_business_profiles_claimed_by ON public.business_profiles(claimed_by);
CREATE INDEX IF NOT EXISTS idx_business_claims_business_id ON public.business_claims(business_id);
CREATE INDEX IF NOT EXISTS idx_business_claims_user_id ON public.business_claims(user_id);
CREATE INDEX IF NOT EXISTS idx_business_claims_status ON public.business_claims(status);
-- Add RLS policies
ALTER TABLE public.business_profiles ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.business_claims ENABLE ROW LEVEL SECURITY;
-- Policies for business_profiles
DROP POLICY IF EXISTS "Public profiles are viewable by everyone" ON public.business_profiles;
CREATE POLICY "Public profiles are viewable by everyone"
ON public.business_profiles FOR SELECT
USING (true);
DROP POLICY IF EXISTS "Profiles can be updated by verified owners" ON public.business_profiles;
CREATE POLICY "Profiles can be updated by verified owners"
ON public.business_profiles FOR UPDATE
USING (auth.uid() = claimed_by AND verification_status = 'verified');
-- Policies for business_claims
DROP POLICY IF EXISTS "Users can view their own claims" ON public.business_claims;
CREATE POLICY "Users can view their own claims"
ON public.business_claims FOR SELECT
USING (auth.uid() = user_id);
DROP POLICY IF EXISTS "Users can create claims" ON public.business_claims;
CREATE POLICY "Users can create claims"
ON public.business_claims FOR INSERT
WITH CHECK (auth.uid() = user_id);
DROP POLICY IF EXISTS "Only admins can review claims" ON public.business_claims;
CREATE POLICY "Only admins can review claims"
ON public.business_claims FOR UPDATE
USING (EXISTS (
SELECT 1 FROM auth.users
WHERE auth.uid() = id
AND raw_app_meta_data->>'role' = 'admin'
));
END;
$$ LANGUAGE plpgsql;

View File

@ -0,0 +1,63 @@
-- Function to review business claims
CREATE OR REPLACE FUNCTION review_business_claim(
p_claim_id UUID,
p_business_id TEXT,
p_user_id UUID,
p_status TEXT,
p_notes TEXT DEFAULT NULL
) RETURNS void AS $$
BEGIN
-- Start transaction
BEGIN
-- Update claim status
UPDATE public.business_claims
SET
status = p_status,
reviewed_at = CURRENT_TIMESTAMP,
reviewed_by = p_user_id,
notes = COALESCE(p_notes, notes)
WHERE id = p_claim_id;
-- If approved, update business profile
IF p_status = 'approved' THEN
-- Get the user_id from the claim
WITH claim_user AS (
SELECT user_id
FROM public.business_claims
WHERE id = p_claim_id
)
INSERT INTO public.business_profiles (
business_id,
claimed_by,
claimed_at,
verification_status
)
SELECT
p_business_id,
user_id,
CURRENT_TIMESTAMP,
'verified'
FROM claim_user
ON CONFLICT (business_id)
DO UPDATE SET
claimed_by = EXCLUDED.claimed_by,
claimed_at = EXCLUDED.claimed_at,
verification_status = EXCLUDED.verification_status;
END IF;
-- Reject any other pending claims for this business
IF p_status = 'approved' THEN
UPDATE public.business_claims
SET
status = 'rejected',
reviewed_at = CURRENT_TIMESTAMP,
reviewed_by = p_user_id,
notes = COALESCE(notes, '') || E'\nAutomatically rejected due to another approved claim.'
WHERE
business_id = p_business_id
AND id != p_claim_id
AND status = 'pending';
END IF;
END;
END;
$$ LANGUAGE plpgsql;

View File

@ -0,0 +1,37 @@
-- Insert test business
INSERT INTO public.businesses (
id,
name,
phone,
email,
address,
rating,
website,
description,
source
) VALUES (
'test-business-1',
'Test Coffee Shop',
'303-555-0123',
'contact@testcoffee.com',
'123 Test St, Denver, CO 80202',
4.5,
'https://testcoffee.com',
'A cozy coffee shop in downtown Denver serving artisanal coffee and pastries.',
'manual'
) ON CONFLICT (id) DO NOTHING;
-- Insert test business profile
INSERT INTO public.business_profiles (
business_id,
verification_status,
social_links,
hours_of_operation,
tags
) VALUES (
'test-business-1',
'unverified',
'{"facebook": "https://facebook.com/testcoffee", "instagram": "https://instagram.com/testcoffee"}',
'{"monday": ["7:00", "19:00"], "tuesday": ["7:00", "19:00"], "wednesday": ["7:00", "19:00"], "thursday": ["7:00", "19:00"], "friday": ["7:00", "20:00"], "saturday": ["8:00", "20:00"], "sunday": ["8:00", "18:00"]}',
ARRAY['coffee', 'pastries', 'breakfast', 'lunch']
) ON CONFLICT (business_id) DO NOTHING;