| """ |
| Run this script once to create the Supabase users table. |
| Usage: python scripts/migrate_supabase.py |
| |
| Requires the SUPABASE_URL and SUPABASE_SERVICE_KEY env vars to be set. |
| This uses the Supabase REST API to execute SQL via the pg_query extension. |
| If that fails, manually run migrations/001_create_users.sql in your Supabase SQL editor. |
| """ |
| import os |
| import sys |
|
|
| import httpx |
|
|
| SQL = """ |
| CREATE TABLE IF NOT EXISTS users ( |
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), |
| hf_username TEXT UNIQUE NOT NULL, |
| hf_email TEXT DEFAULT '', |
| hf_avatar_url TEXT DEFAULT '', |
| created_at TIMESTAMPTZ DEFAULT now(), |
| last_login_at TIMESTAMPTZ DEFAULT now(), |
| onboarding_complete BOOLEAN DEFAULT false |
| ); |
| |
| CREATE INDEX IF NOT EXISTS idx_users_hf_username ON users (hf_username); |
| |
| -- Enable RLS |
| ALTER TABLE users ENABLE ROW LEVEL SECURITY; |
| |
| -- Drop existing policies if they exist and recreate |
| DROP POLICY IF EXISTS "Users can read own row" ON users; |
| CREATE POLICY "Users can read own row" ON users |
| FOR SELECT USING (hf_username = current_setting('request.jwt.claims', true)::json->>'hf_username'); |
| |
| DROP POLICY IF EXISTS "Users can update own row" ON users; |
| CREATE POLICY "Users can update own row" ON users |
| FOR UPDATE USING (hf_username = current_setting('request.jwt.claims', true)::json->>'hf_username') |
| WITH CHECK (hf_username = current_setting('request.jwt.claims', true)::json->>'hf_username'); |
| """ |
|
|
|
|
| async def main(): |
| url = os.environ.get("SUPABASE_URL", "").rstrip("/") |
| service_key = os.environ.get("SUPABASE_SERVICE_KEY", "") |
| anon_key = os.environ.get("SUPABASE_ANON_KEY", "") |
|
|
| if not url or not service_key: |
| print("ERROR: SUPABASE_URL and SUPABASE_SERVICE_KEY must be set") |
| print("\nAlternatively, run the SQL in migrations/001_create_users.sql") |
| print("directly in your Supabase dashboard SQL editor.") |
| sys.exit(1) |
|
|
| headers = { |
| "apikey": anon_key or service_key, |
| "Authorization": f"Bearer {service_key}", |
| "Content-Type": "application/json", |
| } |
|
|
| |
| print("Attempting migration via pg_query RPC...") |
| async with httpx.AsyncClient(timeout=15) as client: |
| resp = await client.post( |
| f"{url}/rest/v1/rpc/pg_query", |
| json={"query": SQL}, |
| headers=headers, |
| ) |
| if resp.status_code == 200: |
| print("Migration successful via pg_query RPC.") |
| return |
|
|
| |
| print(f"pg_query RPC failed ({resp.status_code}), trying /sql endpoint...") |
|
|
| resp2 = await client.post( |
| f"{url}/sql", |
| json={"query": SQL}, |
| headers=headers, |
| ) |
| if resp2.status_code == 200: |
| print("Migration successful via /sql endpoint.") |
| return |
|
|
| print(f"/sql endpoint failed ({resp2.status_code})") |
| print(f"Response: {resp2.text[:200]}") |
| print("\nPlease run the SQL in migrations/001_create_users.sql") |
| print("manually in your Supabase dashboard SQL editor.") |
| print(f"URL: {url}/project/default/sql/new") |
|
|
|
|
| if __name__ == "__main__": |
| import asyncio |
|
|
| asyncio.run(main()) |
|
|