diff --git a/supabase/migrations/02-image-and-pricing.sql b/supabase/migrations/02-image-and-pricing.sql new file mode 100644 index 0000000..4fa9dbd --- /dev/null +++ b/supabase/migrations/02-image-and-pricing.sql @@ -0,0 +1,26 @@ +-- ============================================================ +-- Migration 02 — image column on ads + price_history tracking +-- ============================================================ + +-- ----------------------------------------------------------- +-- 1. Add main_image_url to ads (nullable) +-- ----------------------------------------------------------- +ALTER TABLE ads + ADD COLUMN IF NOT EXISTS main_image_url TEXT; + +-- ----------------------------------------------------------- +-- 2. price_history — record every price change per ad +-- ----------------------------------------------------------- +CREATE TABLE IF NOT EXISTS price_history ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + ad_id uuid NOT NULL REFERENCES ads(id) ON DELETE CASCADE, + old_price numeric NOT NULL, + new_price numeric NOT NULL, + changed_at timestamptz NOT NULL DEFAULT now(), + UNIQUE (ad_id, old_price, new_price) +); + +CREATE INDEX IF NOT EXISTS idx_price_history_ad_id + ON price_history(ad_id); + +-- Note: supabase_admin role creation + grants moved to post-boot.sql. diff --git a/supabase/migrations/03-global-keywords.sql b/supabase/migrations/03-global-keywords.sql new file mode 100644 index 0000000..f1f31ea --- /dev/null +++ b/supabase/migrations/03-global-keywords.sql @@ -0,0 +1,74 @@ +CREATE TABLE IF NOT EXISTS keywords ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + keyword text NOT NULL, + interval_minutes int NOT NULL DEFAULT 60, + is_active boolean NOT NULL DEFAULT true, + last_scraped_at timestamptz, + initial_loaded boolean NOT NULL DEFAULT false, + created_at timestamptz NOT NULL DEFAULT now() +); + +CREATE UNIQUE INDEX IF NOT EXISTS idx_keywords_unique_lower ON keywords (LOWER(keyword)); + +CREATE TABLE IF NOT EXISTS keyword_subscriptions ( + keyword_id uuid NOT NULL REFERENCES keywords(id) ON DELETE CASCADE, + user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, + subscribed_at timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY (keyword_id, user_id) +); + +CREATE INDEX IF NOT EXISTS idx_keyword_subscriptions_user ON keyword_subscriptions(user_id); + +ALTER TABLE ads ADD COLUMN IF NOT EXISTS postcode TEXT; +ALTER TABLE ads ADD COLUMN IF NOT EXISTS modified_at TIMESTAMPTZ; + +DO $$ +DECLARE + sq_record RECORD; + kw_id uuid; +BEGIN + FOR sq_record IN SELECT DISTINCT LOWER(keyword) AS keyword, interval_minutes, is_active, last_scraped_at FROM search_queries LOOP + INSERT INTO keywords (keyword, interval_minutes, is_active, last_scraped_at) + VALUES (sq_record.keyword, sq_record.interval_minutes, sq_record.is_active, sq_record.last_scraped_at) + ON CONFLICT DO NOTHING; + END LOOP; + + FOR sq_record IN SELECT user_id, LOWER(keyword) AS keyword FROM search_queries LOOP + SELECT id INTO kw_id FROM keywords WHERE LOWER(keyword) = sq_record.keyword LIMIT 1; + IF kw_id IS NOT NULL THEN + INSERT INTO keyword_subscriptions (keyword_id, user_id) + VALUES (kw_id, sq_record.user_id) + ON CONFLICT DO NOTHING; + END IF; + END LOOP; +END $$; + +UPDATE keywords SET initial_loaded = true +WHERE id IN ( + SELECT DISTINCT kw.id + FROM keywords kw + INNER JOIN search_queries sq ON LOWER(sq.keyword) = LOWER(kw.keyword) + WHERE EXISTS ( + SELECT 1 FROM scrape_logs sl + WHERE sl.search_query_id = sq.id AND sl.status = 'success' + ) +); + +ALTER TABLE scrape_logs DROP CONSTRAINT IF EXISTS scrape_logs_search_query_id_fkey; +ALTER TABLE scrape_logs RENAME COLUMN search_query_id TO keyword_id; + +UPDATE scrape_logs sl SET keyword_id = kw.id +FROM search_queries sq, keywords kw +WHERE sl.keyword_id = sq.id + AND LOWER(sq.keyword) = LOWER(kw.keyword); + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'fk_scrape_logs_keyword') THEN + ALTER TABLE scrape_logs ADD CONSTRAINT fk_scrape_logs_keyword + FOREIGN KEY (keyword_id) REFERENCES keywords(id) ON DELETE CASCADE; + END IF; +END $$; + +DROP TABLE IF EXISTS query_ads CASCADE; +DROP TABLE IF EXISTS search_queries CASCADE;