diff --git a/supabase/migrations/20240704190621_initial_migration.sql b/supabase/migrations/20240704190621_initial_migration.sql new file mode 100644 index 0000000..240c5a0 --- /dev/null +++ b/supabase/migrations/20240704190621_initial_migration.sql @@ -0,0 +1,159 @@ +/** +* USERS +* Note: This table contains user data. Users should only be able to view and update their own data. +*/ +create table users ( + -- UUID from auth.users + id uuid references auth.users not null primary key, + full_name text, + avatar_url text, + -- The customer's billing address, stored in JSON format. + billing_address jsonb, + -- Stores your customer's payment instruments. + payment_method jsonb +); +alter table users + enable row level security; +create policy "Can view own user data." on users + for select using ((select auth.uid()) = id); +create policy "Can update own user data." on users + for update using ((select auth.uid()) = id); + +/** +* This trigger automatically creates a user entry when a new user signs up via Supabase Auth. +*/ +create function public.handle_new_user() +returns trigger as +$$ + begin + insert into public.users (id, full_name, avatar_url) + values (new.id, new.raw_user_meta_data->>'full_name', new.raw_user_meta_data->>'avatar_url'); + return new; + end; +$$ +language plpgsql security definer; + +create trigger on_auth_user_created + after insert on auth.users + for each row + execute procedure public.handle_new_user(); + +/** +* CUSTOMERS +* Note: this is a private table that contains a mapping of user IDs to Stripe customer IDs. +*/ +create table customers ( + -- UUID from auth.users + id uuid references auth.users not null primary key, + -- The user's customer ID in Stripe. User must not be able to update this. + stripe_customer_id text +); +alter table customers enable row level security; +-- No policies as this is a private table that the user must not have access to. + +/** +* PRODUCTS +* Note: products are created and managed in Stripe and synced to our DB via Stripe webhooks. +*/ +create table products ( + -- Product ID from Stripe, e.g. prod_1234. + id text primary key, + -- Whether the product is currently available for purchase. + active boolean, + -- The product's name, meant to be displayable to the customer. Whenever this product is sold via a subscription, name will show up on associated invoice line item descriptions. + name text, + -- The product's description, meant to be displayable to the customer. Use this field to optionally store a long form explanation of the product being sold for your own rendering purposes. + description text, + -- A URL of the product image in Stripe, meant to be displayable to the customer. + image text, + -- Set of key-value pairs, used to store additional information about the object in a structured format. + metadata jsonb +); +alter table products + enable row level security; +create policy "Allow public read-only access." on products + for select using (true); + +/** +* PRICES +* Note: prices are created and managed in Stripe and synced to our DB via Stripe webhooks. +*/ +create type pricing_type as enum ('one_time', 'recurring'); +create type pricing_plan_interval as enum ('day', 'week', 'month', 'year'); +create table prices ( + -- Price ID from Stripe, e.g. price_1234. + id text primary key, + -- The ID of the prduct that this price belongs to. + product_id text references products, + -- Whether the price can be used for new purchases. + active boolean, + -- A brief description of the price. + description text, + -- The unit amount as a positive integer in the smallest currency unit (e.g., 100 cents for US$1.00 or 100 for ¥100, a zero-decimal currency). + unit_amount bigint, + -- Three-letter ISO currency code, in lowercase. + currency text check (char_length(currency) = 3), + -- One of `one_time` or `recurring` depending on whether the price is for a one-time purchase or a recurring (subscription) purchase. + type pricing_type, + -- The frequency at which a subscription is billed. One of `day`, `week`, `month` or `year`. + interval pricing_plan_interval, + -- The number of intervals (specified in the `interval` attribute) between subscription billings. For example, `interval=month` and `interval_count=3` bills every 3 months. + interval_count integer, + -- Default number of trial days when subscribing a customer to this price using [`trial_from_plan=true`](https://stripe.com/docs/api#create_subscription-trial_from_plan). + trial_period_days integer, + -- Set of key-value pairs, used to store additional information about the object in a structured format. + metadata jsonb +); +alter table prices + enable row level security; +create policy "Allow public read-only access." on prices + for select using (true); + +/** +* SUBSCRIPTIONS +* Note: subscriptions are created and managed in Stripe and synced to our DB via Stripe webhooks. +*/ +create type subscription_status as enum ('trialing', 'active', 'canceled', 'incomplete', 'incomplete_expired', 'past_due', 'unpaid'); +create table subscriptions ( + -- Subscription ID from Stripe, e.g. sub_1234. + id text primary key, + user_id uuid references auth.users not null, + -- The status of the subscription object, one of subscription_status type above. + status subscription_status, + -- Set of key-value pairs, used to store additional information about the object in a structured format. + metadata jsonb, + -- ID of the price that created this subscription. + price_id text references prices, + -- Quantity multiplied by the unit amount of the price creates the amount of the subscription. Can be used to charge multiple seats. + quantity integer, + -- If true the subscription has been canceled by the user and will be deleted at the end of the billing period. + cancel_at_period_end boolean, + -- Time at which the subscription was created. + created timestamp with time zone default timezone('utc'::text, now()) not null, + -- Start of the current period that the subscription has been invoiced for. + current_period_start timestamp with time zone default timezone('utc'::text, now()) not null, + -- End of the current period that the subscription has been invoiced for. At the end of this period, a new invoice will be created. + current_period_end timestamp with time zone default timezone('utc'::text, now()) not null, + -- If the subscription has ended, the timestamp of the date the subscription ended. + ended_at timestamp with time zone default timezone('utc'::text, now()), + -- A date in the future at which the subscription will automatically get canceled. + cancel_at timestamp with time zone default timezone('utc'::text, now()), + -- If the subscription has been canceled, the date of that cancellation. If the subscription was canceled with `cancel_at_period_end`, `canceled_at` will still reflect the date of the initial cancellation request, not the end of the subscription period when the subscription is automatically moved to a canceled state. + canceled_at timestamp with time zone default timezone('utc'::text, now()), + -- If the subscription has a trial, the beginning of that trial. + trial_start timestamp with time zone default timezone('utc'::text, now()), + -- If the subscription has a trial, the end of that trial. + trial_end timestamp with time zone default timezone('utc'::text, now()) +); +alter table subscriptions + enable row level security; +create policy "Can only view own subs data." on subscriptions + for select using ((select auth.uid()) = user_id); + +/** + * REALTIME SUBSCRIPTIONS + * Only allow realtime listening on public tables. + */ +drop publication if exists supabase_realtime; +create publication supabase_realtime + for table products, prices; \ No newline at end of file