Some checks failed
ci / ci (22, ubuntu-latest) (push) Has been cancelled
Nuxt 4 + Supabase + Flightics API. Incluye búsqueda de vuelos, inspiraciones, watchlist, tracking de precios y mapa interactivo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
152 lines
4.6 KiB
PL/PgSQL
152 lines
4.6 KiB
PL/PgSQL
-- ============================================
|
|
-- Vuelato: Schema completo
|
|
-- Se ejecuta despues de que la imagen supabase/postgres
|
|
-- haya creado los roles y el schema auth internamente.
|
|
-- ============================================
|
|
|
|
-- Tablas de cache (publicas)
|
|
|
|
CREATE TABLE IF NOT EXISTS airports (
|
|
iata TEXT PRIMARY KEY,
|
|
icao TEXT,
|
|
name TEXT NOT NULL,
|
|
lat DOUBLE PRECISION,
|
|
lon DOUBLE PRECISION,
|
|
city_id TEXT,
|
|
city_name TEXT,
|
|
country_code TEXT,
|
|
country_name TEXT,
|
|
region_slug TEXT,
|
|
updated_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS countries (
|
|
iso_code2 TEXT PRIMARY KEY,
|
|
iso_code3 TEXT,
|
|
name_eng TEXT,
|
|
name_native TEXT,
|
|
phone_prefix TEXT,
|
|
updated_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS regions (
|
|
slug TEXT PRIMARY KEY,
|
|
name TEXT,
|
|
name_localized TEXT,
|
|
airport_codes TEXT[],
|
|
updated_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS inspirations_cache (
|
|
id SERIAL PRIMARY KEY,
|
|
from_airport TEXT NOT NULL,
|
|
to_airports TEXT[] NOT NULL,
|
|
min_price NUMERIC(10,2),
|
|
min_stops INTEGER,
|
|
fetched_at TIMESTAMPTZ DEFAULT now(),
|
|
UNIQUE(from_airport, to_airports)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS multi_city_cache (
|
|
id SERIAL PRIMARY KEY,
|
|
origin_codes TEXT[] NOT NULL,
|
|
from_airport TEXT,
|
|
stops TEXT[] NOT NULL,
|
|
min_price NUMERIC(10,2),
|
|
fetched_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
-- Tablas de usuario (con RLS)
|
|
|
|
CREATE TABLE IF NOT EXISTS profiles (
|
|
id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
home_airports TEXT[] DEFAULT '{}',
|
|
default_adults INTEGER DEFAULT 1,
|
|
default_children INTEGER DEFAULT 0,
|
|
default_infants INTEGER DEFAULT 0,
|
|
locale TEXT DEFAULT 'es',
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS watchlist (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
booking_token TEXT NOT NULL,
|
|
route_summary TEXT NOT NULL,
|
|
departure_code TEXT,
|
|
arrival_code TEXT,
|
|
departure_date TEXT,
|
|
original_price NUMERIC(10,2) NOT NULL,
|
|
current_price NUMERIC(10,2),
|
|
price_status TEXT DEFAULT 'saved',
|
|
passengers_adult INTEGER DEFAULT 1,
|
|
passengers_child INTEGER DEFAULT 0,
|
|
passengers_infant INTEGER DEFAULT 0,
|
|
last_checked_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS recent_searches (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
search_params JSONB NOT NULL,
|
|
route_summary TEXT,
|
|
search_mode TEXT DEFAULT 'roundtrip',
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS price_alerts (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
watchlist_id UUID REFERENCES watchlist(id) ON DELETE CASCADE,
|
|
target_price NUMERIC(10,2),
|
|
is_active BOOLEAN DEFAULT true,
|
|
last_notified_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
-- Indices
|
|
CREATE INDEX IF NOT EXISTS idx_airports_country ON airports(country_code);
|
|
CREATE INDEX IF NOT EXISTS idx_airports_city ON airports(city_id);
|
|
CREATE INDEX IF NOT EXISTS idx_inspirations_from ON inspirations_cache(from_airport);
|
|
CREATE INDEX IF NOT EXISTS idx_watchlist_user ON watchlist(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_recent_searches_user ON recent_searches(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_price_alerts_user ON price_alerts(user_id);
|
|
|
|
-- RLS
|
|
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE watchlist ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE recent_searches ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE price_alerts ENABLE ROW LEVEL SECURITY;
|
|
|
|
CREATE POLICY "Users see own profile" ON profiles
|
|
FOR ALL USING (auth.uid() = id);
|
|
CREATE POLICY "Users see own watchlist" ON watchlist
|
|
FOR ALL USING (auth.uid() = user_id);
|
|
CREATE POLICY "Users see own searches" ON recent_searches
|
|
FOR ALL USING (auth.uid() = user_id);
|
|
CREATE POLICY "Users see own alerts" ON price_alerts
|
|
FOR ALL USING (auth.uid() = user_id);
|
|
|
|
-- Grants
|
|
GRANT USAGE ON SCHEMA public TO anon, authenticated;
|
|
GRANT SELECT ON airports, countries, regions, inspirations_cache, multi_city_cache
|
|
TO anon, authenticated;
|
|
GRANT ALL ON profiles, watchlist, recent_searches, price_alerts
|
|
TO authenticated;
|
|
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public
|
|
TO anon, authenticated;
|
|
|
|
-- Trigger: crear profile automaticamente al registrarse
|
|
CREATE OR REPLACE FUNCTION public.handle_new_user()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
INSERT INTO public.profiles (id) VALUES (NEW.id);
|
|
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();
|