mirror of
https://github.com/jcreek/SvelteKitSaasBoilerplate.git
synced 2026-07-16 04:23:48 +00:00
feat(#29): Add ability to record that a user has purchased a product - part 2
This commit is contained in:
@@ -16,11 +16,11 @@ To get the types for the tables, you can follow these instructions taken from [h
|
|||||||
|
|
||||||
`npx supabase init`
|
`npx supabase init`
|
||||||
|
|
||||||
Replace `$PROJECT_REF` with your project reference:
|
Replace `$PROJECT_REF` with your project reference and replace `types/supabase.d.ts` with whatever file you want your types in:
|
||||||
`npx supabase gen types typescript --project-id "$PROJECT_REF" --schema public > types/supabase.ts`
|
`npx supabase gen types typescript --project-id "$PROJECT_REF" --schema public > src/lib/types/supabase.d.ts`
|
||||||
|
|
||||||
Replace `types/supabase.d.ts` with whatever file you want your types in:
|
If you are using a local Supabase instance, you can use the following command to generate the types:
|
||||||
`npx supabase gen types typescript --local > types/supabase.d.ts`
|
`npx supabase gen types typescript --local > src/lib/types/supabase.d.ts`
|
||||||
|
|
||||||
## Local DB Development
|
## Local DB Development
|
||||||
|
|
||||||
|
|||||||
Vendored
+300
-548
@@ -1,639 +1,391 @@
|
|||||||
export type Json = string | number | boolean | null | { [key: string]: Json | undefined } | Json[];
|
export type Json =
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| boolean
|
||||||
|
| null
|
||||||
|
| { [key: string]: Json | undefined }
|
||||||
|
| Json[]
|
||||||
|
|
||||||
export type Database = {
|
export type Database = {
|
||||||
graphql_public: {
|
graphql_public: {
|
||||||
Tables: {
|
Tables: {
|
||||||
[_ in never]: never;
|
[_ in never]: never
|
||||||
};
|
}
|
||||||
Views: {
|
Views: {
|
||||||
[_ in never]: never;
|
[_ in never]: never
|
||||||
};
|
}
|
||||||
Functions: {
|
Functions: {
|
||||||
graphql: {
|
graphql: {
|
||||||
Args: {
|
Args: {
|
||||||
operationName?: string;
|
operationName?: string
|
||||||
query?: string;
|
query?: string
|
||||||
variables?: Json;
|
variables?: Json
|
||||||
extensions?: Json;
|
extensions?: Json
|
||||||
};
|
}
|
||||||
Returns: Json;
|
Returns: Json
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
Enums: {
|
Enums: {
|
||||||
[_ in never]: never;
|
[_ in never]: never
|
||||||
};
|
}
|
||||||
CompositeTypes: {
|
CompositeTypes: {
|
||||||
[_ in never]: never;
|
[_ in never]: never
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
public: {
|
public: {
|
||||||
Tables: {
|
Tables: {
|
||||||
customers: {
|
customers: {
|
||||||
Row: {
|
Row: {
|
||||||
id: string;
|
id: string
|
||||||
stripe_customer_id: string | null;
|
stripe_customer_id: string | null
|
||||||
};
|
}
|
||||||
Insert: {
|
Insert: {
|
||||||
id: string;
|
id: string
|
||||||
stripe_customer_id?: string | null;
|
stripe_customer_id?: string | null
|
||||||
};
|
}
|
||||||
Update: {
|
Update: {
|
||||||
id?: string;
|
id?: string
|
||||||
stripe_customer_id?: string | null;
|
stripe_customer_id?: string | null
|
||||||
};
|
}
|
||||||
Relationships: [
|
Relationships: [
|
||||||
{
|
{
|
||||||
foreignKeyName: 'customers_id_fkey';
|
foreignKeyName: "customers_id_fkey"
|
||||||
columns: ['id'];
|
columns: ["id"]
|
||||||
isOneToOne: true;
|
isOneToOne: true
|
||||||
referencedRelation: 'users';
|
referencedRelation: "users"
|
||||||
referencedColumns: ['id'];
|
referencedColumns: ["id"]
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
|
||||||
};
|
|
||||||
prices: {
|
prices: {
|
||||||
Row: {
|
Row: {
|
||||||
active: boolean | null;
|
active: boolean | null
|
||||||
currency: string | null;
|
currency: string | null
|
||||||
description: string | null;
|
description: string | null
|
||||||
id: string;
|
id: string
|
||||||
interval: Database['public']['Enums']['pricing_plan_interval'] | null;
|
interval: Database["public"]["Enums"]["pricing_plan_interval"] | null
|
||||||
interval_count: number | null;
|
interval_count: number | null
|
||||||
metadata: Json | null;
|
metadata: Json | null
|
||||||
product_id: string;
|
product_id: string
|
||||||
trial_period_days: number | null;
|
trial_period_days: number | null
|
||||||
type: Database['public']['Enums']['pricing_type'] | null;
|
type: Database["public"]["Enums"]["pricing_type"] | null
|
||||||
unit_amount: number | null;
|
unit_amount: number | null
|
||||||
};
|
}
|
||||||
Insert: {
|
Insert: {
|
||||||
active?: boolean | null;
|
active?: boolean | null
|
||||||
currency?: string | null;
|
currency?: string | null
|
||||||
description?: string | null;
|
description?: string | null
|
||||||
id: string;
|
id: string
|
||||||
interval?: Database['public']['Enums']['pricing_plan_interval'] | null;
|
interval?: Database["public"]["Enums"]["pricing_plan_interval"] | null
|
||||||
interval_count?: number | null;
|
interval_count?: number | null
|
||||||
metadata?: Json | null;
|
metadata?: Json | null
|
||||||
product_id: string;
|
product_id: string
|
||||||
trial_period_days?: number | null;
|
trial_period_days?: number | null
|
||||||
type?: Database['public']['Enums']['pricing_type'] | null;
|
type?: Database["public"]["Enums"]["pricing_type"] | null
|
||||||
unit_amount?: number | null;
|
unit_amount?: number | null
|
||||||
};
|
}
|
||||||
Update: {
|
Update: {
|
||||||
active?: boolean | null;
|
active?: boolean | null
|
||||||
currency?: string | null;
|
currency?: string | null
|
||||||
description?: string | null;
|
description?: string | null
|
||||||
id?: string;
|
id?: string
|
||||||
interval?: Database['public']['Enums']['pricing_plan_interval'] | null;
|
interval?: Database["public"]["Enums"]["pricing_plan_interval"] | null
|
||||||
interval_count?: number | null;
|
interval_count?: number | null
|
||||||
metadata?: Json | null;
|
metadata?: Json | null
|
||||||
product_id?: string;
|
product_id?: string
|
||||||
trial_period_days?: number | null;
|
trial_period_days?: number | null
|
||||||
type?: Database['public']['Enums']['pricing_type'] | null;
|
type?: Database["public"]["Enums"]["pricing_type"] | null
|
||||||
unit_amount?: number | null;
|
unit_amount?: number | null
|
||||||
};
|
}
|
||||||
Relationships: [
|
Relationships: [
|
||||||
{
|
{
|
||||||
foreignKeyName: 'prices_product_id_fkey';
|
foreignKeyName: "prices_product_id_fkey"
|
||||||
columns: ['product_id'];
|
columns: ["product_id"]
|
||||||
isOneToOne: false;
|
isOneToOne: false
|
||||||
referencedRelation: 'products';
|
referencedRelation: "products"
|
||||||
referencedColumns: ['id'];
|
referencedColumns: ["id"]
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
|
||||||
};
|
|
||||||
products: {
|
products: {
|
||||||
Row: {
|
Row: {
|
||||||
active: boolean | null;
|
active: boolean | null
|
||||||
created_at: string;
|
created_at: string
|
||||||
description: string | null;
|
description: string | null
|
||||||
features: string[] | null;
|
features: string[] | null
|
||||||
id: string;
|
id: string
|
||||||
images: string[] | null;
|
images: string[] | null
|
||||||
metadata: Json | null;
|
metadata: Json | null
|
||||||
name: string | null;
|
name: string | null
|
||||||
updated_at: string;
|
updated_at: string
|
||||||
};
|
}
|
||||||
Insert: {
|
Insert: {
|
||||||
active?: boolean | null;
|
active?: boolean | null
|
||||||
created_at?: string;
|
created_at?: string
|
||||||
description?: string | null;
|
description?: string | null
|
||||||
features?: string[] | null;
|
features?: string[] | null
|
||||||
id: string;
|
id: string
|
||||||
images?: string[] | null;
|
images?: string[] | null
|
||||||
metadata?: Json | null;
|
metadata?: Json | null
|
||||||
name?: string | null;
|
name?: string | null
|
||||||
updated_at?: string;
|
updated_at?: string
|
||||||
};
|
}
|
||||||
Update: {
|
Update: {
|
||||||
active?: boolean | null;
|
active?: boolean | null
|
||||||
created_at?: string;
|
created_at?: string
|
||||||
description?: string | null;
|
description?: string | null
|
||||||
features?: string[] | null;
|
features?: string[] | null
|
||||||
id?: string;
|
id?: string
|
||||||
images?: string[] | null;
|
images?: string[] | null
|
||||||
metadata?: Json | null;
|
metadata?: Json | null
|
||||||
name?: string | null;
|
name?: string | null
|
||||||
updated_at?: string;
|
updated_at?: string
|
||||||
};
|
}
|
||||||
Relationships: [];
|
Relationships: []
|
||||||
};
|
}
|
||||||
|
purchases: {
|
||||||
|
Row: {
|
||||||
|
id: string
|
||||||
|
price_id: string
|
||||||
|
product_id: string
|
||||||
|
purchased_at: string
|
||||||
|
user_id: string
|
||||||
|
}
|
||||||
|
Insert: {
|
||||||
|
id?: string
|
||||||
|
price_id: string
|
||||||
|
product_id: string
|
||||||
|
purchased_at?: string
|
||||||
|
user_id: string
|
||||||
|
}
|
||||||
|
Update: {
|
||||||
|
id?: string
|
||||||
|
price_id?: string
|
||||||
|
product_id?: string
|
||||||
|
purchased_at?: string
|
||||||
|
user_id?: string
|
||||||
|
}
|
||||||
|
Relationships: [
|
||||||
|
{
|
||||||
|
foreignKeyName: "purchases_price_id_fkey"
|
||||||
|
columns: ["price_id"]
|
||||||
|
isOneToOne: false
|
||||||
|
referencedRelation: "prices"
|
||||||
|
referencedColumns: ["id"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
foreignKeyName: "purchases_product_id_fkey"
|
||||||
|
columns: ["product_id"]
|
||||||
|
isOneToOne: false
|
||||||
|
referencedRelation: "products"
|
||||||
|
referencedColumns: ["id"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
foreignKeyName: "purchases_user_id_fkey"
|
||||||
|
columns: ["user_id"]
|
||||||
|
isOneToOne: false
|
||||||
|
referencedRelation: "users"
|
||||||
|
referencedColumns: ["id"]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
subscriptions: {
|
subscriptions: {
|
||||||
Row: {
|
Row: {
|
||||||
cancel_at: string | null;
|
cancel_at: string | null
|
||||||
cancel_at_period_end: boolean | null;
|
cancel_at_period_end: boolean | null
|
||||||
canceled_at: string | null;
|
canceled_at: string | null
|
||||||
created: string;
|
created: string
|
||||||
current_period_end: string;
|
current_period_end: string
|
||||||
current_period_start: string;
|
current_period_start: string
|
||||||
ended_at: string | null;
|
ended_at: string | null
|
||||||
id: string;
|
id: string
|
||||||
metadata: Json | null;
|
metadata: Json | null
|
||||||
price_id: string | null;
|
price_id: string | null
|
||||||
quantity: number | null;
|
quantity: number | null
|
||||||
status: Database['public']['Enums']['subscription_status'] | null;
|
status: Database["public"]["Enums"]["subscription_status"] | null
|
||||||
trial_end: string | null;
|
trial_end: string | null
|
||||||
trial_start: string | null;
|
trial_start: string | null
|
||||||
user_id: string;
|
user_id: string
|
||||||
};
|
}
|
||||||
Insert: {
|
Insert: {
|
||||||
cancel_at?: string | null;
|
cancel_at?: string | null
|
||||||
cancel_at_period_end?: boolean | null;
|
cancel_at_period_end?: boolean | null
|
||||||
canceled_at?: string | null;
|
canceled_at?: string | null
|
||||||
created?: string;
|
created?: string
|
||||||
current_period_end?: string;
|
current_period_end?: string
|
||||||
current_period_start?: string;
|
current_period_start?: string
|
||||||
ended_at?: string | null;
|
ended_at?: string | null
|
||||||
id: string;
|
id: string
|
||||||
metadata?: Json | null;
|
metadata?: Json | null
|
||||||
price_id?: string | null;
|
price_id?: string | null
|
||||||
quantity?: number | null;
|
quantity?: number | null
|
||||||
status?: Database['public']['Enums']['subscription_status'] | null;
|
status?: Database["public"]["Enums"]["subscription_status"] | null
|
||||||
trial_end?: string | null;
|
trial_end?: string | null
|
||||||
trial_start?: string | null;
|
trial_start?: string | null
|
||||||
user_id: string;
|
user_id: string
|
||||||
};
|
}
|
||||||
Update: {
|
Update: {
|
||||||
cancel_at?: string | null;
|
cancel_at?: string | null
|
||||||
cancel_at_period_end?: boolean | null;
|
cancel_at_period_end?: boolean | null
|
||||||
canceled_at?: string | null;
|
canceled_at?: string | null
|
||||||
created?: string;
|
created?: string
|
||||||
current_period_end?: string;
|
current_period_end?: string
|
||||||
current_period_start?: string;
|
current_period_start?: string
|
||||||
ended_at?: string | null;
|
ended_at?: string | null
|
||||||
id?: string;
|
id?: string
|
||||||
metadata?: Json | null;
|
metadata?: Json | null
|
||||||
price_id?: string | null;
|
price_id?: string | null
|
||||||
quantity?: number | null;
|
quantity?: number | null
|
||||||
status?: Database['public']['Enums']['subscription_status'] | null;
|
status?: Database["public"]["Enums"]["subscription_status"] | null
|
||||||
trial_end?: string | null;
|
trial_end?: string | null
|
||||||
trial_start?: string | null;
|
trial_start?: string | null
|
||||||
user_id?: string;
|
user_id?: string
|
||||||
};
|
}
|
||||||
Relationships: [
|
Relationships: [
|
||||||
{
|
{
|
||||||
foreignKeyName: 'subscriptions_price_id_fkey';
|
foreignKeyName: "subscriptions_price_id_fkey"
|
||||||
columns: ['price_id'];
|
columns: ["price_id"]
|
||||||
isOneToOne: false;
|
isOneToOne: false
|
||||||
referencedRelation: 'prices';
|
referencedRelation: "prices"
|
||||||
referencedColumns: ['id'];
|
referencedColumns: ["id"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
foreignKeyName: 'subscriptions_user_id_fkey';
|
foreignKeyName: "subscriptions_user_id_fkey"
|
||||||
columns: ['user_id'];
|
columns: ["user_id"]
|
||||||
isOneToOne: false;
|
isOneToOne: false
|
||||||
referencedRelation: 'users';
|
referencedRelation: "users"
|
||||||
referencedColumns: ['id'];
|
referencedColumns: ["id"]
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
|
||||||
};
|
|
||||||
users: {
|
users: {
|
||||||
Row: {
|
Row: {
|
||||||
id: string;
|
id: string
|
||||||
name: string | null;
|
name: string | null
|
||||||
};
|
|
||||||
Insert: {
|
|
||||||
id: string;
|
|
||||||
name?: string | null;
|
|
||||||
};
|
|
||||||
Update: {
|
|
||||||
id?: string;
|
|
||||||
name?: string | null;
|
|
||||||
};
|
|
||||||
Relationships: [
|
|
||||||
{
|
|
||||||
foreignKeyName: 'users_id_fkey';
|
|
||||||
columns: ['id'];
|
|
||||||
isOneToOne: true;
|
|
||||||
referencedRelation: 'users';
|
|
||||||
referencedColumns: ['id'];
|
|
||||||
}
|
}
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
Views: {
|
|
||||||
[_ in never]: never;
|
|
||||||
};
|
|
||||||
Functions: {
|
|
||||||
[_ in never]: never;
|
|
||||||
};
|
|
||||||
Enums: {
|
|
||||||
pricing_plan_interval: 'day' | 'week' | 'month' | 'year';
|
|
||||||
pricing_type: 'one_time' | 'recurring';
|
|
||||||
subscription_status:
|
|
||||||
| 'trialing'
|
|
||||||
| 'active'
|
|
||||||
| 'canceled'
|
|
||||||
| 'incomplete'
|
|
||||||
| 'incomplete_expired'
|
|
||||||
| 'past_due'
|
|
||||||
| 'unpaid'
|
|
||||||
| 'paused';
|
|
||||||
};
|
|
||||||
CompositeTypes: {
|
|
||||||
[_ in never]: never;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
storage: {
|
|
||||||
Tables: {
|
|
||||||
buckets: {
|
|
||||||
Row: {
|
|
||||||
allowed_mime_types: string[] | null;
|
|
||||||
avif_autodetection: boolean | null;
|
|
||||||
created_at: string | null;
|
|
||||||
file_size_limit: number | null;
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
owner: string | null;
|
|
||||||
owner_id: string | null;
|
|
||||||
public: boolean | null;
|
|
||||||
updated_at: string | null;
|
|
||||||
};
|
|
||||||
Insert: {
|
Insert: {
|
||||||
allowed_mime_types?: string[] | null;
|
id: string
|
||||||
avif_autodetection?: boolean | null;
|
name?: string | null
|
||||||
created_at?: string | null;
|
|
||||||
file_size_limit?: number | null;
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
owner?: string | null;
|
|
||||||
owner_id?: string | null;
|
|
||||||
public?: boolean | null;
|
|
||||||
updated_at?: string | null;
|
|
||||||
};
|
|
||||||
Update: {
|
|
||||||
allowed_mime_types?: string[] | null;
|
|
||||||
avif_autodetection?: boolean | null;
|
|
||||||
created_at?: string | null;
|
|
||||||
file_size_limit?: number | null;
|
|
||||||
id?: string;
|
|
||||||
name?: string;
|
|
||||||
owner?: string | null;
|
|
||||||
owner_id?: string | null;
|
|
||||||
public?: boolean | null;
|
|
||||||
updated_at?: string | null;
|
|
||||||
};
|
|
||||||
Relationships: [];
|
|
||||||
};
|
|
||||||
migrations: {
|
|
||||||
Row: {
|
|
||||||
executed_at: string | null;
|
|
||||||
hash: string;
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
Insert: {
|
|
||||||
executed_at?: string | null;
|
|
||||||
hash: string;
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
Update: {
|
|
||||||
executed_at?: string | null;
|
|
||||||
hash?: string;
|
|
||||||
id?: number;
|
|
||||||
name?: string;
|
|
||||||
};
|
|
||||||
Relationships: [];
|
|
||||||
};
|
|
||||||
objects: {
|
|
||||||
Row: {
|
|
||||||
bucket_id: string | null;
|
|
||||||
created_at: string | null;
|
|
||||||
id: string;
|
|
||||||
last_accessed_at: string | null;
|
|
||||||
metadata: Json | null;
|
|
||||||
name: string | null;
|
|
||||||
owner: string | null;
|
|
||||||
owner_id: string | null;
|
|
||||||
path_tokens: string[] | null;
|
|
||||||
updated_at: string | null;
|
|
||||||
version: string | null;
|
|
||||||
};
|
|
||||||
Insert: {
|
|
||||||
bucket_id?: string | null;
|
|
||||||
created_at?: string | null;
|
|
||||||
id?: string;
|
|
||||||
last_accessed_at?: string | null;
|
|
||||||
metadata?: Json | null;
|
|
||||||
name?: string | null;
|
|
||||||
owner?: string | null;
|
|
||||||
owner_id?: string | null;
|
|
||||||
path_tokens?: string[] | null;
|
|
||||||
updated_at?: string | null;
|
|
||||||
version?: string | null;
|
|
||||||
};
|
|
||||||
Update: {
|
|
||||||
bucket_id?: string | null;
|
|
||||||
created_at?: string | null;
|
|
||||||
id?: string;
|
|
||||||
last_accessed_at?: string | null;
|
|
||||||
metadata?: Json | null;
|
|
||||||
name?: string | null;
|
|
||||||
owner?: string | null;
|
|
||||||
owner_id?: string | null;
|
|
||||||
path_tokens?: string[] | null;
|
|
||||||
updated_at?: string | null;
|
|
||||||
version?: string | null;
|
|
||||||
};
|
|
||||||
Relationships: [
|
|
||||||
{
|
|
||||||
foreignKeyName: 'objects_bucketId_fkey';
|
|
||||||
columns: ['bucket_id'];
|
|
||||||
isOneToOne: false;
|
|
||||||
referencedRelation: 'buckets';
|
|
||||||
referencedColumns: ['id'];
|
|
||||||
}
|
}
|
||||||
];
|
|
||||||
};
|
|
||||||
s3_multipart_uploads: {
|
|
||||||
Row: {
|
|
||||||
bucket_id: string;
|
|
||||||
created_at: string;
|
|
||||||
id: string;
|
|
||||||
in_progress_size: number;
|
|
||||||
key: string;
|
|
||||||
owner_id: string | null;
|
|
||||||
upload_signature: string;
|
|
||||||
version: string;
|
|
||||||
};
|
|
||||||
Insert: {
|
|
||||||
bucket_id: string;
|
|
||||||
created_at?: string;
|
|
||||||
id: string;
|
|
||||||
in_progress_size?: number;
|
|
||||||
key: string;
|
|
||||||
owner_id?: string | null;
|
|
||||||
upload_signature: string;
|
|
||||||
version: string;
|
|
||||||
};
|
|
||||||
Update: {
|
Update: {
|
||||||
bucket_id?: string;
|
id?: string
|
||||||
created_at?: string;
|
name?: string | null
|
||||||
id?: string;
|
|
||||||
in_progress_size?: number;
|
|
||||||
key?: string;
|
|
||||||
owner_id?: string | null;
|
|
||||||
upload_signature?: string;
|
|
||||||
version?: string;
|
|
||||||
};
|
|
||||||
Relationships: [
|
|
||||||
{
|
|
||||||
foreignKeyName: 's3_multipart_uploads_bucket_id_fkey';
|
|
||||||
columns: ['bucket_id'];
|
|
||||||
isOneToOne: false;
|
|
||||||
referencedRelation: 'buckets';
|
|
||||||
referencedColumns: ['id'];
|
|
||||||
}
|
}
|
||||||
];
|
|
||||||
};
|
|
||||||
s3_multipart_uploads_parts: {
|
|
||||||
Row: {
|
|
||||||
bucket_id: string;
|
|
||||||
created_at: string;
|
|
||||||
etag: string;
|
|
||||||
id: string;
|
|
||||||
key: string;
|
|
||||||
owner_id: string | null;
|
|
||||||
part_number: number;
|
|
||||||
size: number;
|
|
||||||
upload_id: string;
|
|
||||||
version: string;
|
|
||||||
};
|
|
||||||
Insert: {
|
|
||||||
bucket_id: string;
|
|
||||||
created_at?: string;
|
|
||||||
etag: string;
|
|
||||||
id?: string;
|
|
||||||
key: string;
|
|
||||||
owner_id?: string | null;
|
|
||||||
part_number: number;
|
|
||||||
size?: number;
|
|
||||||
upload_id: string;
|
|
||||||
version: string;
|
|
||||||
};
|
|
||||||
Update: {
|
|
||||||
bucket_id?: string;
|
|
||||||
created_at?: string;
|
|
||||||
etag?: string;
|
|
||||||
id?: string;
|
|
||||||
key?: string;
|
|
||||||
owner_id?: string | null;
|
|
||||||
part_number?: number;
|
|
||||||
size?: number;
|
|
||||||
upload_id?: string;
|
|
||||||
version?: string;
|
|
||||||
};
|
|
||||||
Relationships: [
|
Relationships: [
|
||||||
{
|
{
|
||||||
foreignKeyName: 's3_multipart_uploads_parts_bucket_id_fkey';
|
foreignKeyName: "users_id_fkey"
|
||||||
columns: ['bucket_id'];
|
columns: ["id"]
|
||||||
isOneToOne: false;
|
isOneToOne: true
|
||||||
referencedRelation: 'buckets';
|
referencedRelation: "users"
|
||||||
referencedColumns: ['id'];
|
referencedColumns: ["id"]
|
||||||
},
|
},
|
||||||
{
|
]
|
||||||
foreignKeyName: 's3_multipart_uploads_parts_upload_id_fkey';
|
}
|
||||||
columns: ['upload_id'];
|
|
||||||
isOneToOne: false;
|
|
||||||
referencedRelation: 's3_multipart_uploads';
|
|
||||||
referencedColumns: ['id'];
|
|
||||||
}
|
}
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
Views: {
|
Views: {
|
||||||
[_ in never]: never;
|
[_ in never]: never
|
||||||
};
|
}
|
||||||
Functions: {
|
Functions: {
|
||||||
can_insert_object: {
|
[_ in never]: never
|
||||||
Args: {
|
}
|
||||||
bucketid: string;
|
|
||||||
name: string;
|
|
||||||
owner: string;
|
|
||||||
metadata: Json;
|
|
||||||
};
|
|
||||||
Returns: undefined;
|
|
||||||
};
|
|
||||||
extension: {
|
|
||||||
Args: {
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
Returns: string;
|
|
||||||
};
|
|
||||||
filename: {
|
|
||||||
Args: {
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
Returns: string;
|
|
||||||
};
|
|
||||||
foldername: {
|
|
||||||
Args: {
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
Returns: string[];
|
|
||||||
};
|
|
||||||
get_size_by_bucket: {
|
|
||||||
Args: Record<PropertyKey, never>;
|
|
||||||
Returns: {
|
|
||||||
size: number;
|
|
||||||
bucket_id: string;
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
list_multipart_uploads_with_delimiter: {
|
|
||||||
Args: {
|
|
||||||
bucket_id: string;
|
|
||||||
prefix_param: string;
|
|
||||||
delimiter_param: string;
|
|
||||||
max_keys?: number;
|
|
||||||
next_key_token?: string;
|
|
||||||
next_upload_token?: string;
|
|
||||||
};
|
|
||||||
Returns: {
|
|
||||||
key: string;
|
|
||||||
id: string;
|
|
||||||
created_at: string;
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
list_objects_with_delimiter: {
|
|
||||||
Args: {
|
|
||||||
bucket_id: string;
|
|
||||||
prefix_param: string;
|
|
||||||
delimiter_param: string;
|
|
||||||
max_keys?: number;
|
|
||||||
start_after?: string;
|
|
||||||
next_token?: string;
|
|
||||||
};
|
|
||||||
Returns: {
|
|
||||||
name: string;
|
|
||||||
id: string;
|
|
||||||
metadata: Json;
|
|
||||||
updated_at: string;
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
operation: {
|
|
||||||
Args: Record<PropertyKey, never>;
|
|
||||||
Returns: string;
|
|
||||||
};
|
|
||||||
search: {
|
|
||||||
Args: {
|
|
||||||
prefix: string;
|
|
||||||
bucketname: string;
|
|
||||||
limits?: number;
|
|
||||||
levels?: number;
|
|
||||||
offsets?: number;
|
|
||||||
search?: string;
|
|
||||||
sortcolumn?: string;
|
|
||||||
sortorder?: string;
|
|
||||||
};
|
|
||||||
Returns: {
|
|
||||||
name: string;
|
|
||||||
id: string;
|
|
||||||
updated_at: string;
|
|
||||||
created_at: string;
|
|
||||||
last_accessed_at: string;
|
|
||||||
metadata: Json;
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
Enums: {
|
Enums: {
|
||||||
[_ in never]: never;
|
pricing_plan_interval: "day" | "week" | "month" | "year"
|
||||||
};
|
pricing_type: "one_time" | "recurring"
|
||||||
|
subscription_status:
|
||||||
|
| "trialing"
|
||||||
|
| "active"
|
||||||
|
| "canceled"
|
||||||
|
| "incomplete"
|
||||||
|
| "incomplete_expired"
|
||||||
|
| "past_due"
|
||||||
|
| "unpaid"
|
||||||
|
| "paused"
|
||||||
|
}
|
||||||
CompositeTypes: {
|
CompositeTypes: {
|
||||||
[_ in never]: never;
|
[_ in never]: never
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
type PublicSchema = Database[Extract<keyof Database, 'public'>];
|
type PublicSchema = Database[Extract<keyof Database, "public">]
|
||||||
|
|
||||||
export type Tables<
|
export type Tables<
|
||||||
PublicTableNameOrOptions extends
|
PublicTableNameOrOptions extends
|
||||||
| keyof (PublicSchema['Tables'] & PublicSchema['Views'])
|
| keyof (PublicSchema["Tables"] & PublicSchema["Views"])
|
||||||
| { schema: keyof Database },
|
| { schema: keyof Database },
|
||||||
TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
|
TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
|
||||||
? keyof (Database[PublicTableNameOrOptions['schema']]['Tables'] &
|
? keyof (Database[PublicTableNameOrOptions["schema"]]["Tables"] &
|
||||||
Database[PublicTableNameOrOptions['schema']]['Views'])
|
Database[PublicTableNameOrOptions["schema"]]["Views"])
|
||||||
: never = never
|
: never = never,
|
||||||
> = PublicTableNameOrOptions extends { schema: keyof Database }
|
> = PublicTableNameOrOptions extends { schema: keyof Database }
|
||||||
? (Database[PublicTableNameOrOptions['schema']]['Tables'] &
|
? (Database[PublicTableNameOrOptions["schema"]]["Tables"] &
|
||||||
Database[PublicTableNameOrOptions['schema']]['Views'])[TableName] extends {
|
Database[PublicTableNameOrOptions["schema"]]["Views"])[TableName] extends {
|
||||||
Row: infer R;
|
Row: infer R
|
||||||
}
|
}
|
||||||
? R
|
? R
|
||||||
: never
|
: never
|
||||||
: PublicTableNameOrOptions extends keyof (PublicSchema['Tables'] & PublicSchema['Views'])
|
: PublicTableNameOrOptions extends keyof (PublicSchema["Tables"] &
|
||||||
? (PublicSchema['Tables'] & PublicSchema['Views'])[PublicTableNameOrOptions] extends {
|
PublicSchema["Views"])
|
||||||
Row: infer R;
|
? (PublicSchema["Tables"] &
|
||||||
|
PublicSchema["Views"])[PublicTableNameOrOptions] extends {
|
||||||
|
Row: infer R
|
||||||
}
|
}
|
||||||
? R
|
? R
|
||||||
: never
|
: never
|
||||||
: never;
|
: never
|
||||||
|
|
||||||
export type TablesInsert<
|
export type TablesInsert<
|
||||||
PublicTableNameOrOptions extends keyof PublicSchema['Tables'] | { schema: keyof Database },
|
PublicTableNameOrOptions extends
|
||||||
|
| keyof PublicSchema["Tables"]
|
||||||
|
| { schema: keyof Database },
|
||||||
TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
|
TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
|
||||||
? keyof Database[PublicTableNameOrOptions['schema']]['Tables']
|
? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"]
|
||||||
: never = never
|
: never = never,
|
||||||
> = PublicTableNameOrOptions extends { schema: keyof Database }
|
> = PublicTableNameOrOptions extends { schema: keyof Database }
|
||||||
? Database[PublicTableNameOrOptions['schema']]['Tables'][TableName] extends {
|
? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends {
|
||||||
Insert: infer I;
|
Insert: infer I
|
||||||
}
|
}
|
||||||
? I
|
? I
|
||||||
: never
|
: never
|
||||||
: PublicTableNameOrOptions extends keyof PublicSchema['Tables']
|
: PublicTableNameOrOptions extends keyof PublicSchema["Tables"]
|
||||||
? PublicSchema['Tables'][PublicTableNameOrOptions] extends {
|
? PublicSchema["Tables"][PublicTableNameOrOptions] extends {
|
||||||
Insert: infer I;
|
Insert: infer I
|
||||||
}
|
}
|
||||||
? I
|
? I
|
||||||
: never
|
: never
|
||||||
: never;
|
: never
|
||||||
|
|
||||||
export type TablesUpdate<
|
export type TablesUpdate<
|
||||||
PublicTableNameOrOptions extends keyof PublicSchema['Tables'] | { schema: keyof Database },
|
PublicTableNameOrOptions extends
|
||||||
|
| keyof PublicSchema["Tables"]
|
||||||
|
| { schema: keyof Database },
|
||||||
TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
|
TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
|
||||||
? keyof Database[PublicTableNameOrOptions['schema']]['Tables']
|
? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"]
|
||||||
: never = never
|
: never = never,
|
||||||
> = PublicTableNameOrOptions extends { schema: keyof Database }
|
> = PublicTableNameOrOptions extends { schema: keyof Database }
|
||||||
? Database[PublicTableNameOrOptions['schema']]['Tables'][TableName] extends {
|
? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends {
|
||||||
Update: infer U;
|
Update: infer U
|
||||||
}
|
}
|
||||||
? U
|
? U
|
||||||
: never
|
: never
|
||||||
: PublicTableNameOrOptions extends keyof PublicSchema['Tables']
|
: PublicTableNameOrOptions extends keyof PublicSchema["Tables"]
|
||||||
? PublicSchema['Tables'][PublicTableNameOrOptions] extends {
|
? PublicSchema["Tables"][PublicTableNameOrOptions] extends {
|
||||||
Update: infer U;
|
Update: infer U
|
||||||
}
|
}
|
||||||
? U
|
? U
|
||||||
: never
|
: never
|
||||||
: never;
|
: never
|
||||||
|
|
||||||
export type Enums<
|
export type Enums<
|
||||||
PublicEnumNameOrOptions extends keyof PublicSchema['Enums'] | { schema: keyof Database },
|
PublicEnumNameOrOptions extends
|
||||||
|
| keyof PublicSchema["Enums"]
|
||||||
|
| { schema: keyof Database },
|
||||||
EnumName extends PublicEnumNameOrOptions extends { schema: keyof Database }
|
EnumName extends PublicEnumNameOrOptions extends { schema: keyof Database }
|
||||||
? keyof Database[PublicEnumNameOrOptions['schema']]['Enums']
|
? keyof Database[PublicEnumNameOrOptions["schema"]]["Enums"]
|
||||||
: never = never
|
: never = never,
|
||||||
> = PublicEnumNameOrOptions extends { schema: keyof Database }
|
> = PublicEnumNameOrOptions extends { schema: keyof Database }
|
||||||
? Database[PublicEnumNameOrOptions['schema']]['Enums'][EnumName]
|
? Database[PublicEnumNameOrOptions["schema"]]["Enums"][EnumName]
|
||||||
: PublicEnumNameOrOptions extends keyof PublicSchema['Enums']
|
: PublicEnumNameOrOptions extends keyof PublicSchema["Enums"]
|
||||||
? PublicSchema['Enums'][PublicEnumNameOrOptions]
|
? PublicSchema["Enums"][PublicEnumNameOrOptions]
|
||||||
: never;
|
: never
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ const toDateTime = (secs: number) => {
|
|||||||
return t;
|
return t;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
type Product = Tables<'products'>;
|
type Product = Tables<'products'>;
|
||||||
type Price = Tables<'prices'>;
|
type Price = Tables<'prices'>;
|
||||||
|
|
||||||
@@ -245,11 +244,32 @@ const manageSubscriptionStatusChange = async (
|
|||||||
// }
|
// }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const recordProductPurchase = async (userId: string, productId: string, priceId: string) => {
|
||||||
|
console.log(
|
||||||
|
`Recording product purchase for user [${userId}] and product [${productId}] with price [${priceId}]`
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { error } = await supabaseAdmin
|
||||||
|
.from('purchases')
|
||||||
|
.insert([{ user_id: userId, product_id: productId, price_id: priceId }]);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw new Error(`Failed to record product purchase: ${error.message}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Product purchased recorded for user [${userId}] and product [${productId}]`);
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
upsertProductRecord,
|
upsertProductRecord,
|
||||||
upsertPriceRecord,
|
upsertPriceRecord,
|
||||||
deleteProductRecord,
|
deleteProductRecord,
|
||||||
deletePriceRecord,
|
deletePriceRecord,
|
||||||
createOrRetrieveCustomer,
|
createOrRetrieveCustomer,
|
||||||
manageSubscriptionStatusChange
|
manageSubscriptionStatusChange,
|
||||||
|
recordProductPurchase
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import {
|
|||||||
deleteProductRecord,
|
deleteProductRecord,
|
||||||
manageSubscriptionStatusChange,
|
manageSubscriptionStatusChange,
|
||||||
upsertPriceRecord,
|
upsertPriceRecord,
|
||||||
upsertProductRecord
|
upsertProductRecord,
|
||||||
|
recordProductPurchase
|
||||||
} from '$lib/utils/supabase/admin';
|
} from '$lib/utils/supabase/admin';
|
||||||
import { stripe as stripeClient } from '$lib/utils/stripe';
|
import { stripe as stripeClient } from '$lib/utils/stripe';
|
||||||
|
|
||||||
@@ -34,7 +35,6 @@ export const POST: RequestHandler = async ({ request }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
// Product events
|
|
||||||
case 'product.created':
|
case 'product.created':
|
||||||
case 'product.updated':
|
case 'product.updated':
|
||||||
await upsertProductRecord(event.data.object as stripe.Product);
|
await upsertProductRecord(event.data.object as stripe.Product);
|
||||||
@@ -64,6 +64,7 @@ export const POST: RequestHandler = async ({ request }) => {
|
|||||||
}
|
}
|
||||||
case 'checkout.session.completed': {
|
case 'checkout.session.completed': {
|
||||||
const checkoutSession = event.data.object as stripe.Checkout.Session;
|
const checkoutSession = event.data.object as stripe.Checkout.Session;
|
||||||
|
|
||||||
if (checkoutSession.mode === 'subscription') {
|
if (checkoutSession.mode === 'subscription') {
|
||||||
const subscriptionId = checkoutSession.subscription;
|
const subscriptionId = checkoutSession.subscription;
|
||||||
await manageSubscriptionStatusChange(
|
await manageSubscriptionStatusChange(
|
||||||
@@ -72,8 +73,33 @@ export const POST: RequestHandler = async ({ request }) => {
|
|||||||
true
|
true
|
||||||
);
|
);
|
||||||
} else if (checkoutSession.mode === 'payment') {
|
} else if (checkoutSession.mode === 'payment') {
|
||||||
// TODO Record that they purchased the product
|
// Fetch session with expanded line_items
|
||||||
console.log(checkoutSession);
|
const sessionWithLineItems = await stripeClient.checkout.sessions.retrieve(
|
||||||
|
checkoutSession.id,
|
||||||
|
{
|
||||||
|
expand: ['line_items']
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const lineItems = sessionWithLineItems.line_items?.data;
|
||||||
|
const userId = checkoutSession.metadata!.userId;
|
||||||
|
|
||||||
|
if (!lineItems) {
|
||||||
|
throw new Error('No line items found in session');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userId) {
|
||||||
|
throw new Error('No user ID found in session metadata');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const item of lineItems ?? []) {
|
||||||
|
const productId = item.price?.product as string;
|
||||||
|
const priceId = item.price?.id as string;
|
||||||
|
|
||||||
|
if (productId && priceId) {
|
||||||
|
await recordProductPurchase(userId, productId, priceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,3 +135,19 @@ create table subscriptions (
|
|||||||
);
|
);
|
||||||
alter table subscriptions enable row level security;
|
alter table subscriptions enable row level security;
|
||||||
create policy "Can only view own subs data." on subscriptions for select using (auth.uid() = user_id);
|
create policy "Can only view own subs data." on subscriptions for select using (auth.uid() = user_id);
|
||||||
|
|
||||||
|
-- Purchases table to record product purchases.
|
||||||
|
create table purchases (
|
||||||
|
id uuid default uuid_generate_v4() primary key,
|
||||||
|
user_id uuid references auth.users not null,
|
||||||
|
product_id text references products(id) not null,
|
||||||
|
price_id text references prices(id) not null,
|
||||||
|
purchased_at timestamp with time zone not null default now()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Enabling row-level security for purchases.
|
||||||
|
alter table purchases enable row level security;
|
||||||
|
|
||||||
|
-- Creating a policy to ensure that users can only view their own purchase data.
|
||||||
|
create policy "Can view own purchase data." on purchases for select using (auth.uid() = user_id);
|
||||||
|
create policy "Can insert own purchase data." on purchases for insert with check (auth.uid() = user_id);
|
||||||
|
|||||||
Reference in New Issue
Block a user