Optimized agents rules subfolders, dependencies updates (#355)
* Update AGENTS.md and CLAUDE.md for improved clarity and structure * Added MCP Server * Added missing triggers to tables that should have used them * Updated all dependencies * Fixed rare bug in React present in the Admin layout which prevents navigating to pages (sometimes...)
This commit is contained in:
committed by
GitHub
parent
9fae142f2d
commit
533dfba5b9
292
apps/web/supabase/AGENTS.md
Normal file
292
apps/web/supabase/AGENTS.md
Normal file
@@ -0,0 +1,292 @@
|
||||
# Supabase Database Schema Management
|
||||
|
||||
This file contains guidance for working with database schemas, migrations, and Supabase development workflows.
|
||||
|
||||
## Schema Organization
|
||||
|
||||
Schemas are organized in numbered files in the `schemas/` directory. Numbers are used to sort dependencies.
|
||||
|
||||
## Schema Development Workflow
|
||||
|
||||
### 1. Creating New Schema Files
|
||||
|
||||
```bash
|
||||
# Create new schema file
|
||||
touch schemas/15-my-new-feature.sql
|
||||
|
||||
# Apply changes and create migration
|
||||
pnpm --filter web run supabase:db:diff -f my-new-feature
|
||||
|
||||
# Restart Supabase with fresh schema
|
||||
pnpm supabase:web:reset
|
||||
|
||||
# Generate TypeScript types
|
||||
pnpm supabase:web:typegen
|
||||
```
|
||||
|
||||
### 2. Modifying Existing Schemas
|
||||
|
||||
```bash
|
||||
# Edit schema file (e.g., schemas/03-accounts.sql)
|
||||
# Make your changes...
|
||||
|
||||
# Create migration for changes
|
||||
pnpm --filter web run supabase:db:diff -f update-accounts
|
||||
|
||||
# Apply and test
|
||||
pnpm supabase:web:reset
|
||||
pnpm supabase:web:typegen
|
||||
```
|
||||
|
||||
## Security First Patterns
|
||||
|
||||
## Add permissions (if any)
|
||||
|
||||
```sql
|
||||
ALTER TYPE public.app_permissions ADD VALUE 'notes.manage';
|
||||
COMMIT;
|
||||
```
|
||||
|
||||
### Table Creation with RLS
|
||||
|
||||
```sql
|
||||
-- Create table
|
||||
create table if not exists public.notes (
|
||||
id uuid unique not null default extensions.uuid_generate_v4(),
|
||||
account_id uuid references public.accounts(id) on delete cascade not null,
|
||||
-- ...
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
-- CRITICAL: Always enable RLS
|
||||
alter table "public"."notes" enable row level security;
|
||||
|
||||
-- Revoke default permissions
|
||||
revoke all on public.notes from authenticated, service_role;
|
||||
|
||||
-- Grant specific permissions
|
||||
grant select, insert, update, delete on table public.notes to authenticated;
|
||||
|
||||
-- Add RLS policies
|
||||
create policy "notes_read" on public.notes for select
|
||||
to authenticated using (
|
||||
account_id = (select auth.uid()) or
|
||||
public.has_role_on_account(account_id)
|
||||
);
|
||||
|
||||
create policy "notes_write" on public.notes for insert
|
||||
to authenticated with check (
|
||||
public.has_permission(auth.uid(), account_id, 'notes.manage'::app_permissions)
|
||||
);
|
||||
|
||||
create policy "notes_update" on public.notes for update
|
||||
to authenticated using (
|
||||
public.has_permission(auth.uid(), account_id, 'notes.manage'::app_permissions)
|
||||
)
|
||||
with check (
|
||||
public.has_permission(auth.uid(), account_id, 'notes.manage'::app_permissions)
|
||||
);
|
||||
|
||||
create policy "notes_delete" on public.notes for delete
|
||||
to authenticated using (
|
||||
public.has_permission(auth.uid(), account_id, 'notes.manage'::app_permissions)
|
||||
);
|
||||
```
|
||||
|
||||
### Storage Bucket Policies
|
||||
|
||||
```sql
|
||||
-- Create storage bucket
|
||||
insert into storage.buckets (id, name, public)
|
||||
values ('documents', 'documents', false);
|
||||
|
||||
-- RLS policy for storage
|
||||
create policy documents_policy on storage.objects for all using (
|
||||
bucket_id = 'documents'
|
||||
and (
|
||||
-- File belongs to user's account
|
||||
kit.get_storage_filename_as_uuid(name) = auth.uid()
|
||||
or
|
||||
-- User has access to the account
|
||||
public.has_role_on_account(kit.get_storage_filename_as_uuid(name))
|
||||
)
|
||||
)
|
||||
with check (
|
||||
bucket_id = 'documents'
|
||||
and (
|
||||
kit.get_storage_filename_as_uuid(name) = auth.uid()
|
||||
or
|
||||
public.has_permission(
|
||||
auth.uid(),
|
||||
kit.get_storage_filename_as_uuid(name),
|
||||
'files.upload'::app_permissions
|
||||
)
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
## Function Creation Patterns
|
||||
|
||||
### Safe Security Definer Functions
|
||||
|
||||
```sql
|
||||
-- NEVER create security definer functions without explicit access controls
|
||||
create or replace function public.create_team_account(account_name text)
|
||||
returns public.accounts
|
||||
language plpgsql
|
||||
security definer -- Elevated privileges
|
||||
set search_path = '' -- Prevent SQL injection
|
||||
as $$
|
||||
declare
|
||||
new_account public.accounts;
|
||||
begin
|
||||
-- CRITICAL: Validate permissions first
|
||||
if not public.is_set('enable_team_accounts') then
|
||||
raise exception 'Team accounts are not enabled';
|
||||
end if;
|
||||
|
||||
-- Additional validation can go here
|
||||
if length(account_name) < 3 then
|
||||
raise exception 'Account name must be at least 3 characters';
|
||||
end if;
|
||||
|
||||
-- Now safe to proceed with elevated privileges
|
||||
insert into public.accounts (name, is_personal_account)
|
||||
values (account_name, false)
|
||||
returning * into new_account;
|
||||
|
||||
return new_account;
|
||||
end;
|
||||
$$;
|
||||
|
||||
-- Grant to authenticated users only
|
||||
grant execute on function public.create_team_account(text) to authenticated;
|
||||
```
|
||||
|
||||
### Security Invoker Functions (Safer)
|
||||
|
||||
```sql
|
||||
-- Preferred: Functions that inherit RLS policies
|
||||
create or replace function public.get_account_notes(target_account_id uuid)
|
||||
returns setof public.notes
|
||||
language plpgsql
|
||||
security invoker -- Inherits caller's permissions (RLS applies)
|
||||
set search_path = ''
|
||||
as $$
|
||||
begin
|
||||
-- RLS policies will automatically restrict results
|
||||
return query
|
||||
select * from public.notes
|
||||
where account_id = target_account_id
|
||||
order by created_at desc;
|
||||
end;
|
||||
$$;
|
||||
|
||||
grant execute on function public.get_account_notes(uuid) to authenticated;
|
||||
```
|
||||
|
||||
### Safe Column Additions
|
||||
|
||||
```sql
|
||||
-- Safe: Add nullable columns
|
||||
alter table public.accounts
|
||||
add column if not exists description text;
|
||||
|
||||
-- Safe: Add columns with defaults
|
||||
alter table public.accounts
|
||||
add column if not exists is_verified boolean default false not null;
|
||||
|
||||
-- Unsafe: Adding non-null columns without defaults
|
||||
-- alter table public.accounts add column required_field text not null; -- DON'T DO THIS
|
||||
```
|
||||
|
||||
### Index Management
|
||||
|
||||
```sql
|
||||
-- Create indexes concurrently for large tables
|
||||
create index concurrently if not exists ix_accounts_created_at
|
||||
on public.accounts (created_at desc);
|
||||
|
||||
-- Drop unused indexes
|
||||
drop index if exists ix_old_unused_index;
|
||||
```
|
||||
|
||||
## Testing Database Changes
|
||||
|
||||
### Local Testing
|
||||
|
||||
```bash
|
||||
# Test with fresh database
|
||||
pnpm supabase:web:reset
|
||||
|
||||
# Test your changes
|
||||
pnpm run supabase:web:test
|
||||
```
|
||||
|
||||
## Type Generation
|
||||
|
||||
### After Schema Changes
|
||||
|
||||
```bash
|
||||
# Generate types after any schema changes
|
||||
pnpm supabase:web:typegen
|
||||
# Types are generated to src/lib/supabase/database.types.ts
|
||||
|
||||
# Reset DB
|
||||
pnpm supabase:web:reset
|
||||
```
|
||||
|
||||
### Using Generated Types
|
||||
|
||||
```typescript
|
||||
import { Enums, Tables } from '@kit/supabase/database';
|
||||
|
||||
// Table types
|
||||
type Account = Tables<'accounts'>;
|
||||
type Note = Tables<'notes'>;
|
||||
|
||||
// Enum types
|
||||
type AppPermission = Enums<'app_permissions'>;
|
||||
|
||||
// Insert types
|
||||
type AccountInsert = Tables<'accounts'>['Insert'];
|
||||
type AccountUpdate = Tables<'accounts'>['Update'];
|
||||
|
||||
// Use in functions
|
||||
async function createNote(data: Tables<'notes'>['Insert']) {
|
||||
const { data: note, error } = await supabase
|
||||
.from('notes')
|
||||
.insert(data)
|
||||
.select()
|
||||
.single();
|
||||
|
||||
return note;
|
||||
}
|
||||
```
|
||||
|
||||
## Common Schema Patterns
|
||||
|
||||
### Audit Trail
|
||||
|
||||
Add triggers if the properties exist and are appropriate:
|
||||
|
||||
- `public.trigger_set_timestamps()` - for tables with `created_at` and `updated_at`
|
||||
columns
|
||||
- `public.trigger_set_user_tracking()` - for tables with `created_by` and `updated_by`
|
||||
columns
|
||||
|
||||
### Useful Commands
|
||||
|
||||
```bash
|
||||
# View migration status
|
||||
pnpm --filter web supabase migration list
|
||||
|
||||
# Reset database completely
|
||||
pnpm supabase:web:reset
|
||||
|
||||
# Generate migration from schema diff
|
||||
pnpm --filter web run supabase:db:diff -f migration-name
|
||||
|
||||
# Apply specific migration
|
||||
pnpm --filter web supabase migration up --include-schemas public
|
||||
```
|
||||
292
apps/web/supabase/CLAUDE.md
Normal file
292
apps/web/supabase/CLAUDE.md
Normal file
@@ -0,0 +1,292 @@
|
||||
# Supabase Database Schema Management
|
||||
|
||||
This file contains guidance for working with database schemas, migrations, and Supabase development workflows.
|
||||
|
||||
## Schema Organization
|
||||
|
||||
Schemas are organized in numbered files in the `schemas/` directory. Numbers are used to sort dependencies.
|
||||
|
||||
## Schema Development Workflow
|
||||
|
||||
### 1. Creating New Schema Files
|
||||
|
||||
```bash
|
||||
# Create new schema file
|
||||
touch schemas/15-my-new-feature.sql
|
||||
|
||||
# Apply changes and create migration
|
||||
pnpm --filter web run supabase:db:diff -f my-new-feature
|
||||
|
||||
# Restart Supabase with fresh schema
|
||||
pnpm supabase:web:reset
|
||||
|
||||
# Generate TypeScript types
|
||||
pnpm supabase:web:typegen
|
||||
```
|
||||
|
||||
### 2. Modifying Existing Schemas
|
||||
|
||||
```bash
|
||||
# Edit schema file (e.g., schemas/03-accounts.sql)
|
||||
# Make your changes...
|
||||
|
||||
# Create migration for changes
|
||||
pnpm --filter web run supabase:db:diff -f update-accounts
|
||||
|
||||
# Apply and test
|
||||
pnpm supabase:web:reset
|
||||
pnpm supabase:web:typegen
|
||||
```
|
||||
|
||||
## Security First Patterns
|
||||
|
||||
## Add permissions (if any)
|
||||
|
||||
```sql
|
||||
ALTER TYPE public.app_permissions ADD VALUE 'notes.manage';
|
||||
COMMIT;
|
||||
```
|
||||
|
||||
### Table Creation with RLS
|
||||
|
||||
```sql
|
||||
-- Create table
|
||||
create table if not exists public.notes (
|
||||
id uuid unique not null default extensions.uuid_generate_v4(),
|
||||
account_id uuid references public.accounts(id) on delete cascade not null,
|
||||
-- ...
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
-- CRITICAL: Always enable RLS
|
||||
alter table "public"."notes" enable row level security;
|
||||
|
||||
-- Revoke default permissions
|
||||
revoke all on public.notes from authenticated, service_role;
|
||||
|
||||
-- Grant specific permissions
|
||||
grant select, insert, update, delete on table public.notes to authenticated;
|
||||
|
||||
-- Add RLS policies
|
||||
create policy "notes_read" on public.notes for select
|
||||
to authenticated using (
|
||||
account_id = (select auth.uid()) or
|
||||
public.has_role_on_account(account_id)
|
||||
);
|
||||
|
||||
create policy "notes_write" on public.notes for insert
|
||||
to authenticated with check (
|
||||
public.has_permission(auth.uid(), account_id, 'notes.manage'::app_permissions)
|
||||
);
|
||||
|
||||
create policy "notes_update" on public.notes for update
|
||||
to authenticated using (
|
||||
public.has_permission(auth.uid(), account_id, 'notes.manage'::app_permissions)
|
||||
)
|
||||
with check (
|
||||
public.has_permission(auth.uid(), account_id, 'notes.manage'::app_permissions)
|
||||
);
|
||||
|
||||
create policy "notes_delete" on public.notes for delete
|
||||
to authenticated using (
|
||||
public.has_permission(auth.uid(), account_id, 'notes.manage'::app_permissions)
|
||||
);
|
||||
```
|
||||
|
||||
### Storage Bucket Policies
|
||||
|
||||
```sql
|
||||
-- Create storage bucket
|
||||
insert into storage.buckets (id, name, public)
|
||||
values ('documents', 'documents', false);
|
||||
|
||||
-- RLS policy for storage
|
||||
create policy documents_policy on storage.objects for all using (
|
||||
bucket_id = 'documents'
|
||||
and (
|
||||
-- File belongs to user's account
|
||||
kit.get_storage_filename_as_uuid(name) = auth.uid()
|
||||
or
|
||||
-- User has access to the account
|
||||
public.has_role_on_account(kit.get_storage_filename_as_uuid(name))
|
||||
)
|
||||
)
|
||||
with check (
|
||||
bucket_id = 'documents'
|
||||
and (
|
||||
kit.get_storage_filename_as_uuid(name) = auth.uid()
|
||||
or
|
||||
public.has_permission(
|
||||
auth.uid(),
|
||||
kit.get_storage_filename_as_uuid(name),
|
||||
'files.upload'::app_permissions
|
||||
)
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
## Function Creation Patterns
|
||||
|
||||
### Safe Security Definer Functions
|
||||
|
||||
```sql
|
||||
-- NEVER create security definer functions without explicit access controls
|
||||
create or replace function public.create_team_account(account_name text)
|
||||
returns public.accounts
|
||||
language plpgsql
|
||||
security definer -- Elevated privileges
|
||||
set search_path = '' -- Prevent SQL injection
|
||||
as $$
|
||||
declare
|
||||
new_account public.accounts;
|
||||
begin
|
||||
-- CRITICAL: Validate permissions first
|
||||
if not public.is_set('enable_team_accounts') then
|
||||
raise exception 'Team accounts are not enabled';
|
||||
end if;
|
||||
|
||||
-- Additional validation can go here
|
||||
if length(account_name) < 3 then
|
||||
raise exception 'Account name must be at least 3 characters';
|
||||
end if;
|
||||
|
||||
-- Now safe to proceed with elevated privileges
|
||||
insert into public.accounts (name, is_personal_account)
|
||||
values (account_name, false)
|
||||
returning * into new_account;
|
||||
|
||||
return new_account;
|
||||
end;
|
||||
$$;
|
||||
|
||||
-- Grant to authenticated users only
|
||||
grant execute on function public.create_team_account(text) to authenticated;
|
||||
```
|
||||
|
||||
### Security Invoker Functions (Safer)
|
||||
|
||||
```sql
|
||||
-- Preferred: Functions that inherit RLS policies
|
||||
create or replace function public.get_account_notes(target_account_id uuid)
|
||||
returns setof public.notes
|
||||
language plpgsql
|
||||
security invoker -- Inherits caller's permissions (RLS applies)
|
||||
set search_path = ''
|
||||
as $$
|
||||
begin
|
||||
-- RLS policies will automatically restrict results
|
||||
return query
|
||||
select * from public.notes
|
||||
where account_id = target_account_id
|
||||
order by created_at desc;
|
||||
end;
|
||||
$$;
|
||||
|
||||
grant execute on function public.get_account_notes(uuid) to authenticated;
|
||||
```
|
||||
|
||||
### Safe Column Additions
|
||||
|
||||
```sql
|
||||
-- Safe: Add nullable columns
|
||||
alter table public.accounts
|
||||
add column if not exists description text;
|
||||
|
||||
-- Safe: Add columns with defaults
|
||||
alter table public.accounts
|
||||
add column if not exists is_verified boolean default false not null;
|
||||
|
||||
-- Unsafe: Adding non-null columns without defaults
|
||||
-- alter table public.accounts add column required_field text not null; -- DON'T DO THIS
|
||||
```
|
||||
|
||||
### Index Management
|
||||
|
||||
```sql
|
||||
-- Create indexes concurrently for large tables
|
||||
create index concurrently if not exists ix_accounts_created_at
|
||||
on public.accounts (created_at desc);
|
||||
|
||||
-- Drop unused indexes
|
||||
drop index if exists ix_old_unused_index;
|
||||
```
|
||||
|
||||
## Testing Database Changes
|
||||
|
||||
### Local Testing
|
||||
|
||||
```bash
|
||||
# Test with fresh database
|
||||
pnpm supabase:web:reset
|
||||
|
||||
# Test your changes
|
||||
pnpm run supabase:web:test
|
||||
```
|
||||
|
||||
## Type Generation
|
||||
|
||||
### After Schema Changes
|
||||
|
||||
```bash
|
||||
# Generate types after any schema changes
|
||||
pnpm supabase:web:typegen
|
||||
# Types are generated to src/lib/supabase/database.types.ts
|
||||
|
||||
# Reset DB
|
||||
pnpm supabase:web:reset
|
||||
```
|
||||
|
||||
### Using Generated Types
|
||||
|
||||
```typescript
|
||||
import { Enums, Tables } from '@kit/supabase/database';
|
||||
|
||||
// Table types
|
||||
type Account = Tables<'accounts'>;
|
||||
type Note = Tables<'notes'>;
|
||||
|
||||
// Enum types
|
||||
type AppPermission = Enums<'app_permissions'>;
|
||||
|
||||
// Insert types
|
||||
type AccountInsert = Tables<'accounts'>['Insert'];
|
||||
type AccountUpdate = Tables<'accounts'>['Update'];
|
||||
|
||||
// Use in functions
|
||||
async function createNote(data: Tables<'notes'>['Insert']) {
|
||||
const { data: note, error } = await supabase
|
||||
.from('notes')
|
||||
.insert(data)
|
||||
.select()
|
||||
.single();
|
||||
|
||||
return note;
|
||||
}
|
||||
```
|
||||
|
||||
## Common Schema Patterns
|
||||
|
||||
### Audit Trail
|
||||
|
||||
Add triggers if the properties exist and are appropriate:
|
||||
|
||||
- `public.trigger_set_timestamps()` - for tables with `created_at` and `updated_at`
|
||||
columns
|
||||
- `public.trigger_set_user_tracking()` - for tables with `created_by` and `updated_by`
|
||||
columns
|
||||
|
||||
### Useful Commands
|
||||
|
||||
```bash
|
||||
# View migration status
|
||||
pnpm --filter web supabase migration list
|
||||
|
||||
# Reset database completely
|
||||
pnpm supabase:web:reset
|
||||
|
||||
# Generate migration from schema diff
|
||||
pnpm --filter web run supabase:db:diff -f migration-name
|
||||
|
||||
# Apply specific migration
|
||||
pnpm --filter web supabase migration up --include-schemas public
|
||||
```
|
||||
42
apps/web/supabase/migrations/20250917024249_triggers.sql
Normal file
42
apps/web/supabase/migrations/20250917024249_triggers.sql
Normal file
@@ -0,0 +1,42 @@
|
||||
-- Triggers for accounts table
|
||||
create trigger accounts_set_timestamps
|
||||
before insert or update on public.accounts
|
||||
for each row execute function public.trigger_set_timestamps();
|
||||
|
||||
create trigger accounts_set_user_tracking
|
||||
before insert or update on public.accounts
|
||||
for each row execute function public.trigger_set_user_tracking();
|
||||
|
||||
-- Triggers for accounts_memberships table
|
||||
create trigger accounts_memberships_set_timestamps
|
||||
before insert or update on public.accounts_memberships
|
||||
for each row execute function public.trigger_set_timestamps();
|
||||
|
||||
create trigger accounts_memberships_set_user_tracking
|
||||
before insert or update on public.accounts_memberships
|
||||
for each row execute function public.trigger_set_user_tracking();
|
||||
|
||||
-- Triggers for invitations table
|
||||
create trigger invitations_set_timestamps
|
||||
before insert or update on public.invitations
|
||||
for each row execute function public.trigger_set_timestamps();
|
||||
|
||||
-- Triggers for subscriptions table
|
||||
create trigger subscriptions_set_timestamps
|
||||
before insert or update on public.subscriptions
|
||||
for each row execute function public.trigger_set_timestamps();
|
||||
|
||||
-- Triggers for subscription_items table
|
||||
create trigger subscription_items_set_timestamps
|
||||
before insert or update on public.subscription_items
|
||||
for each row execute function public.trigger_set_timestamps();
|
||||
|
||||
-- Triggers for orders table
|
||||
create trigger orders_set_timestamps
|
||||
before insert or update on public.orders
|
||||
for each row execute function public.trigger_set_timestamps();
|
||||
|
||||
-- Triggers for order_items table
|
||||
create trigger order_items_set_timestamps
|
||||
before insert or update on public.order_items
|
||||
for each row execute function public.trigger_set_timestamps();
|
||||
@@ -77,6 +77,15 @@ create unique index unique_personal_account on public.accounts (primary_owner_us
|
||||
where
|
||||
is_personal_account = true;
|
||||
|
||||
-- Triggers for accounts table
|
||||
create trigger accounts_set_timestamps
|
||||
before insert or update on public.accounts
|
||||
for each row execute function public.trigger_set_timestamps();
|
||||
|
||||
create trigger accounts_set_user_tracking
|
||||
before insert or update on public.accounts
|
||||
for each row execute function public.trigger_set_user_tracking();
|
||||
|
||||
-- RLS on the accounts table
|
||||
-- UPDATE(accounts):
|
||||
-- Team owners can update their accounts
|
||||
|
||||
@@ -46,6 +46,15 @@ create index ix_accounts_memberships_user_id on public.accounts_memberships (use
|
||||
|
||||
create index ix_accounts_memberships_account_role on public.accounts_memberships (account_role);
|
||||
|
||||
-- Triggers for accounts_memberships table
|
||||
create trigger accounts_memberships_set_timestamps
|
||||
before insert or update on public.accounts_memberships
|
||||
for each row execute function public.trigger_set_timestamps();
|
||||
|
||||
create trigger accounts_memberships_set_user_tracking
|
||||
before insert or update on public.accounts_memberships
|
||||
for each row execute function public.trigger_set_user_tracking();
|
||||
|
||||
-- Enable RLS on the accounts_memberships table
|
||||
alter table public.accounts_memberships enable row level security;
|
||||
|
||||
|
||||
@@ -36,6 +36,11 @@ comment on column public.invitations.email is 'The email of the user being invit
|
||||
-- Indexes on the invitations table
|
||||
create index ix_invitations_account_id on public.invitations (account_id);
|
||||
|
||||
-- Triggers for invitations table
|
||||
create trigger invitations_set_timestamps
|
||||
before insert or update on public.invitations
|
||||
for each row execute function public.trigger_set_timestamps();
|
||||
|
||||
-- Revoke all on invitations table from authenticated and service_role
|
||||
revoke all on public.invitations
|
||||
from
|
||||
|
||||
@@ -69,6 +69,11 @@ select
|
||||
-- Indexes on the subscriptions table
|
||||
create index ix_subscriptions_account_id on public.subscriptions (account_id);
|
||||
|
||||
-- Triggers for subscriptions table
|
||||
create trigger subscriptions_set_timestamps
|
||||
before insert or update on public.subscriptions
|
||||
for each row execute function public.trigger_set_timestamps();
|
||||
|
||||
-- Enable RLS on subscriptions table
|
||||
alter table public.subscriptions enable row level security;
|
||||
|
||||
@@ -314,6 +319,11 @@ delete on table public.subscription_items to service_role;
|
||||
-- Indexes on the subscription_items table
|
||||
create index ix_subscription_items_subscription_id on public.subscription_items (subscription_id);
|
||||
|
||||
-- Triggers for subscription_items table
|
||||
create trigger subscription_items_set_timestamps
|
||||
before insert or update on public.subscription_items
|
||||
for each row execute function public.trigger_set_timestamps();
|
||||
|
||||
-- RLS
|
||||
alter table public.subscription_items enable row level security;
|
||||
|
||||
|
||||
@@ -55,6 +55,11 @@ delete on table public.orders to service_role;
|
||||
-- Indexes on the orders table
|
||||
create index ix_orders_account_id on public.orders (account_id);
|
||||
|
||||
-- Triggers for orders table
|
||||
create trigger orders_set_timestamps
|
||||
before insert or update on public.orders
|
||||
for each row execute function public.trigger_set_timestamps();
|
||||
|
||||
-- RLS
|
||||
alter table public.orders enable row level security;
|
||||
|
||||
@@ -130,6 +135,11 @@ grant insert, update, delete on table public.order_items to service_role;
|
||||
-- Indexes on the order_items table
|
||||
create index ix_order_items_order_id on public.order_items (order_id);
|
||||
|
||||
-- Triggers for order_items table
|
||||
create trigger order_items_set_timestamps
|
||||
before insert or update on public.order_items
|
||||
for each row execute function public.trigger_set_timestamps();
|
||||
|
||||
-- RLS
|
||||
alter table public.order_items enable row level security;
|
||||
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
BEGIN;
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select plan(12);
|
||||
|
||||
--- Test the trigger_set_timestamps function on all tables
|
||||
--- This test verifies that created_at and updated_at are properly set on insert
|
||||
|
||||
--- Create test users
|
||||
select tests.create_supabase_user('trigger_test_user1', 'test1@example.com');
|
||||
|
||||
-- Authenticate as test user
|
||||
select makerkit.authenticate_as('trigger_test_user1');
|
||||
|
||||
------------
|
||||
--- Test accounts table timestamp triggers - INSERT
|
||||
------------
|
||||
|
||||
INSERT INTO public.accounts (name, is_personal_account)
|
||||
VALUES ('Test Account', false);
|
||||
|
||||
SELECT ok(
|
||||
(SELECT created_at IS NOT NULL FROM public.accounts WHERE name = 'Test Account'),
|
||||
'accounts: created_at should be set automatically on insert'
|
||||
);
|
||||
|
||||
SELECT ok(
|
||||
(SELECT updated_at IS NOT NULL FROM public.accounts WHERE name = 'Test Account'),
|
||||
'accounts: updated_at should be set automatically on insert'
|
||||
);
|
||||
|
||||
SELECT ok(
|
||||
(SELECT created_at = updated_at FROM public.accounts WHERE name = 'Test Account'),
|
||||
'accounts: created_at should equal updated_at on insert'
|
||||
);
|
||||
|
||||
------------
|
||||
--- Test invitations table timestamp triggers - INSERT
|
||||
------------
|
||||
|
||||
-- Create a team account for invitation testing
|
||||
INSERT INTO public.accounts (name, is_personal_account)
|
||||
VALUES ('Invitation Test Team', false);
|
||||
|
||||
-- Test invitation insert
|
||||
INSERT INTO public.invitations (email, account_id, invited_by, role, invite_token, expires_at)
|
||||
VALUES (
|
||||
'invitee@example.com',
|
||||
(SELECT id FROM public.accounts WHERE name = 'Invitation Test Team'),
|
||||
tests.get_supabase_uid('trigger_test_user1'),
|
||||
'member',
|
||||
'test-token-123',
|
||||
now() + interval '7 days'
|
||||
);
|
||||
|
||||
SELECT ok(
|
||||
(SELECT created_at IS NOT NULL FROM public.invitations WHERE email = 'invitee@example.com'),
|
||||
'invitations: created_at should be set automatically on insert'
|
||||
);
|
||||
|
||||
SELECT ok(
|
||||
(SELECT updated_at IS NOT NULL FROM public.invitations WHERE email = 'invitee@example.com'),
|
||||
'invitations: updated_at should be set automatically on insert'
|
||||
);
|
||||
|
||||
SELECT ok(
|
||||
(SELECT created_at = updated_at FROM public.invitations WHERE email = 'invitee@example.com'),
|
||||
'invitations: created_at should equal updated_at on insert'
|
||||
);
|
||||
|
||||
------------
|
||||
--- Test subscriptions table timestamp triggers - INSERT (service_role required)
|
||||
------------
|
||||
|
||||
set role service_role;
|
||||
|
||||
-- Create billing customer first
|
||||
INSERT INTO public.billing_customers (account_id, provider, customer_id, email)
|
||||
VALUES (
|
||||
(SELECT id FROM public.accounts WHERE name = 'Invitation Test Team'),
|
||||
'stripe',
|
||||
'cus_test123',
|
||||
'billing@example.com'
|
||||
);
|
||||
|
||||
-- Test subscription insert
|
||||
INSERT INTO public.subscriptions (
|
||||
id, account_id, billing_customer_id, status, active, billing_provider,
|
||||
cancel_at_period_end, currency, period_starts_at, period_ends_at
|
||||
)
|
||||
VALUES (
|
||||
'sub_test123',
|
||||
(SELECT id FROM public.accounts WHERE name = 'Invitation Test Team'),
|
||||
(SELECT id FROM public.billing_customers WHERE customer_id = 'cus_test123'),
|
||||
'active',
|
||||
true,
|
||||
'stripe',
|
||||
false,
|
||||
'USD',
|
||||
now(),
|
||||
now() + interval '1 month'
|
||||
);
|
||||
|
||||
SELECT ok(
|
||||
(SELECT created_at IS NOT NULL FROM public.subscriptions WHERE id = 'sub_test123'),
|
||||
'subscriptions: created_at should be set automatically on insert'
|
||||
);
|
||||
|
||||
SELECT ok(
|
||||
(SELECT updated_at IS NOT NULL FROM public.subscriptions WHERE id = 'sub_test123'),
|
||||
'subscriptions: updated_at should be set automatically on insert'
|
||||
);
|
||||
|
||||
SELECT ok(
|
||||
(SELECT created_at = updated_at FROM public.subscriptions WHERE id = 'sub_test123'),
|
||||
'subscriptions: created_at should equal updated_at on insert'
|
||||
);
|
||||
|
||||
------------
|
||||
--- Test subscription_items table timestamp triggers - INSERT
|
||||
------------
|
||||
|
||||
-- Test subscription_item insert
|
||||
INSERT INTO public.subscription_items (
|
||||
id, subscription_id, product_id, variant_id, type, quantity, interval, interval_count
|
||||
)
|
||||
VALUES (
|
||||
'si_test123',
|
||||
'sub_test123',
|
||||
'prod_test123',
|
||||
'var_test123',
|
||||
'flat',
|
||||
1,
|
||||
'month',
|
||||
1
|
||||
);
|
||||
|
||||
SELECT ok(
|
||||
(SELECT created_at IS NOT NULL FROM public.subscription_items WHERE id = 'si_test123'),
|
||||
'subscription_items: created_at should be set automatically on insert'
|
||||
);
|
||||
|
||||
SELECT ok(
|
||||
(SELECT updated_at IS NOT NULL FROM public.subscription_items WHERE id = 'si_test123'),
|
||||
'subscription_items: updated_at should be set automatically on insert'
|
||||
);
|
||||
|
||||
SELECT ok(
|
||||
(SELECT created_at = updated_at FROM public.subscription_items WHERE id = 'si_test123'),
|
||||
'subscription_items: created_at should equal updated_at on insert'
|
||||
);
|
||||
|
||||
SELECT * FROM finish();
|
||||
|
||||
ROLLBACK;
|
||||
@@ -0,0 +1,43 @@
|
||||
BEGIN;
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select plan(3);
|
||||
|
||||
--- Test the trigger_set_user_tracking function on accounts table
|
||||
--- This test verifies that created_by and updated_by are properly set on insert
|
||||
|
||||
--- Create test users
|
||||
select tests.create_supabase_user('user_tracking_test1', 'tracking1@example.com');
|
||||
|
||||
------------
|
||||
--- Test accounts table user tracking triggers - INSERT
|
||||
------------
|
||||
|
||||
-- Authenticate as first user for insert
|
||||
select makerkit.authenticate_as('user_tracking_test1');
|
||||
|
||||
-- Test INSERT: created_by and updated_by should be set to current user
|
||||
INSERT INTO public.accounts (name, is_personal_account)
|
||||
VALUES ('User Tracking Test Account', false);
|
||||
|
||||
SELECT ok(
|
||||
(SELECT created_by = tests.get_supabase_uid('user_tracking_test1')
|
||||
FROM public.accounts WHERE name = 'User Tracking Test Account'),
|
||||
'accounts: created_by should be set to current user on insert'
|
||||
);
|
||||
|
||||
SELECT ok(
|
||||
(SELECT updated_by = tests.get_supabase_uid('user_tracking_test1')
|
||||
FROM public.accounts WHERE name = 'User Tracking Test Account'),
|
||||
'accounts: updated_by should be set to current user on insert'
|
||||
);
|
||||
|
||||
SELECT ok(
|
||||
(SELECT created_by = updated_by
|
||||
FROM public.accounts WHERE name = 'User Tracking Test Account'),
|
||||
'accounts: created_by should equal updated_by on insert'
|
||||
);
|
||||
|
||||
SELECT * FROM finish();
|
||||
|
||||
ROLLBACK;
|
||||
Reference in New Issue
Block a user