opticparse-python / supabase /migrations /001_initial_schema.sql
Nanny7's picture
initial deploy
bcf46c3
Raw
History Blame Contribute Delete
5.27 kB
-- ============================================================================
-- OpticParse & PhishVision — Supabase Database Schema
-- Run this migration in your Supabase SQL Editor (Database → SQL Editor)
-- ============================================================================
-- Enable UUID generation
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
-- ---------------------------------------------------------------------------
-- users: linked to Supabase Auth, stores subscription tier + billing info
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS public.users (
id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
email TEXT NOT NULL UNIQUE,
tier TEXT NOT NULL DEFAULT 'free'
CHECK (tier IN ('free', 'pro', 'enterprise')),
monthly_limit INTEGER NOT NULL DEFAULT 100,
current_usage INTEGER NOT NULL DEFAULT 0,
usage_reset_at TIMESTAMPTZ NOT NULL DEFAULT (date_trunc('month', now()) + interval '1 month'),
lemon_customer_id TEXT,
lemon_subscription_id TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Auto-create a user row when someone signs up via Supabase Auth
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO public.users (id, email)
VALUES (NEW.id, NEW.email);
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users;
CREATE TRIGGER on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();
-- ---------------------------------------------------------------------------
-- api_keys: SHA-256 hashed keys bound to user profiles
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS public.api_keys (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE,
key_hash TEXT NOT NULL UNIQUE,
key_prefix TEXT NOT NULL, -- e.g. 'op_live_a1b2c3d4'
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
revoked_at TIMESTAMPTZ
);
CREATE INDEX IF NOT EXISTS idx_api_keys_hash ON public.api_keys (key_hash) WHERE is_active = true;
CREATE INDEX IF NOT EXISTS idx_api_keys_user ON public.api_keys (user_id);
-- ---------------------------------------------------------------------------
-- usage_logs: per-request audit trail
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS public.usage_logs (
id BIGSERIAL PRIMARY KEY,
user_id UUID NOT NULL REFERENCES public.users(id),
api_key_id UUID REFERENCES public.api_keys(id),
endpoint TEXT NOT NULL,
service TEXT NOT NULL CHECK (service IN ('opticparse', 'phishvision')),
status_code INTEGER,
response_time_ms INTEGER,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_usage_user_month
ON public.usage_logs (user_id, created_at);
-- ---------------------------------------------------------------------------
-- security_events: fraud detection and key suspension log
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS public.security_events (
id BIGSERIAL PRIMARY KEY,
api_key_id UUID REFERENCES public.api_keys(id),
user_id UUID REFERENCES public.users(id),
event_type TEXT NOT NULL
CHECK (event_type IN ('abuse_detected', 'key_suspended', 'key_revoked', 'key_regenerated', 'tier_upgraded', 'tier_downgraded')),
details JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- ---------------------------------------------------------------------------
-- Row Level Security (RLS) policies
-- ---------------------------------------------------------------------------
-- Users can only read their own row
ALTER TABLE public.users ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users read own" ON public.users
FOR SELECT USING (auth.uid() = id);
CREATE POLICY "Users update own" ON public.users
FOR UPDATE USING (auth.uid() = id);
-- API keys: users can only see their own
ALTER TABLE public.api_keys ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Keys read own" ON public.api_keys
FOR SELECT USING (auth.uid() = user_id);
-- Usage logs: users can only see their own
ALTER TABLE public.usage_logs ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Usage read own" ON public.usage_logs
FOR SELECT USING (auth.uid() = user_id);
-- Security events: users can only see their own
ALTER TABLE public.security_events ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Events read own" ON public.security_events
FOR SELECT USING (auth.uid() = user_id);
-- ---------------------------------------------------------------------------
-- Monthly usage reset function (call via Supabase cron or pg_cron)
-- ---------------------------------------------------------------------------
CREATE OR REPLACE FUNCTION public.reset_monthly_usage()
RETURNS void AS $$
BEGIN
UPDATE public.users
SET current_usage = 0,
usage_reset_at = date_trunc('month', now()) + interval '1 month',
updated_at = now()
WHERE usage_reset_at <= now();
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;