-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsupabase-schema.sql
More file actions
153 lines (131 loc) · 5.24 KB
/
Copy pathsupabase-schema.sql
File metadata and controls
153 lines (131 loc) · 5.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
-- LARUN.SPACE Database Schema for Supabase
-- Run this in the Supabase SQL Editor: https://supabase.com/dashboard/project/mwmbcfcvnkwegrjlauis/sql/new
-- ============================================
-- Profiles Table
-- ============================================
CREATE TABLE IF NOT EXISTS profiles (
id UUID REFERENCES auth.users(id) ON DELETE CASCADE PRIMARY KEY,
email TEXT,
name TEXT,
avatar_url TEXT,
tier TEXT DEFAULT 'free' CHECK (tier IN ('free', 'researcher', 'scientist', 'enterprise')),
targets_used INTEGER DEFAULT 0,
api_calls_used INTEGER DEFAULT 0,
usage_reset_date TIMESTAMP WITH TIME ZONE DEFAULT (NOW() + INTERVAL '1 month'),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Enable Row Level Security
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
-- Policies for profiles
CREATE POLICY "Users can view their own profile" ON profiles
FOR SELECT USING (auth.uid() = id);
CREATE POLICY "Users can update their own profile" ON profiles
FOR UPDATE USING (auth.uid() = id);
-- Auto-create profile on user signup
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO public.profiles (id, email, name, avatar_url)
VALUES (
NEW.id,
NEW.email,
COALESCE(NEW.raw_user_meta_data->>'name', NEW.raw_user_meta_data->>'user_name', SPLIT_PART(NEW.email, '@', 1)),
NEW.raw_user_meta_data->>'avatar_url'
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Trigger to create profile on signup
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 Table
-- ============================================
CREATE TABLE IF NOT EXISTS api_keys (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,
name TEXT DEFAULT 'Default Key',
key_hash TEXT NOT NULL,
key_prefix TEXT NOT NULL,
last_used_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Enable Row Level Security
ALTER TABLE api_keys ENABLE ROW LEVEL SECURITY;
-- Policies for api_keys
CREATE POLICY "Users can view their own API keys" ON api_keys
FOR SELECT USING (auth.uid() = user_id);
CREATE POLICY "Users can create their own API keys" ON api_keys
FOR INSERT WITH CHECK (auth.uid() = user_id);
CREATE POLICY "Users can delete their own API keys" ON api_keys
FOR DELETE USING (auth.uid() = user_id);
-- Index for faster lookups
CREATE INDEX IF NOT EXISTS idx_api_keys_user_id ON api_keys(user_id);
CREATE INDEX IF NOT EXISTS idx_api_keys_key_hash ON api_keys(key_hash);
-- ============================================
-- Subscriptions Table (for future Stripe integration)
-- ============================================
CREATE TABLE IF NOT EXISTS subscriptions (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,
stripe_customer_id TEXT,
stripe_subscription_id TEXT,
tier TEXT DEFAULT 'free' CHECK (tier IN ('free', 'researcher', 'scientist', 'enterprise')),
status TEXT DEFAULT 'active' CHECK (status IN ('active', 'canceled', 'past_due', 'trialing')),
current_period_start TIMESTAMP WITH TIME ZONE,
current_period_end TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Enable Row Level Security
ALTER TABLE subscriptions ENABLE ROW LEVEL SECURITY;
-- Policies for subscriptions
CREATE POLICY "Users can view their own subscriptions" ON subscriptions
FOR SELECT USING (auth.uid() = user_id);
-- Index
CREATE INDEX IF NOT EXISTS idx_subscriptions_user_id ON subscriptions(user_id);
-- ============================================
-- Usage Logs Table (for tracking API usage)
-- ============================================
CREATE TABLE IF NOT EXISTS usage_logs (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
api_key_id UUID REFERENCES api_keys(id) ON DELETE SET NULL,
action TEXT NOT NULL,
target_name TEXT,
metadata JSONB DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Enable Row Level Security
ALTER TABLE usage_logs ENABLE ROW LEVEL SECURITY;
-- Policy
CREATE POLICY "Users can view their own usage logs" ON usage_logs
FOR SELECT USING (auth.uid() = user_id);
-- Index
CREATE INDEX IF NOT EXISTS idx_usage_logs_user_id ON usage_logs(user_id);
CREATE INDEX IF NOT EXISTS idx_usage_logs_created_at ON usage_logs(created_at);
-- ============================================
-- Function to validate API key (for Edge Functions)
-- ============================================
CREATE OR REPLACE FUNCTION validate_api_key(key_hash_input TEXT)
RETURNS TABLE (
user_id UUID,
tier TEXT,
is_valid BOOLEAN
) AS $$
BEGIN
RETURN QUERY
SELECT
ak.user_id,
COALESCE(p.tier, 'free') as tier,
TRUE as is_valid
FROM api_keys ak
JOIN profiles p ON p.id = ak.user_id
WHERE ak.key_hash = key_hash_input;
-- Update last_used_at
UPDATE api_keys SET last_used_at = NOW() WHERE api_keys.key_hash = key_hash_input;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;