refactor: consolidate AGENTS.md and CLAUDE.md files, update tech stac… (#444)

* refactor: consolidate AGENTS.md and CLAUDE.md files, update tech stack and architecture details

- Merged content from CLAUDE.md into AGENTS.md for better organization.
- Updated tech stack section to reflect the current technologies used, including Next.js, Supabase, and Tailwind CSS.
- Enhanced monorepo structure documentation with detailed directory purposes.
- Streamlined multi-tenant architecture explanation and essential commands.
- Added key patterns for naming conventions and server actions.
- Removed outdated agent files related to Playwright and PostgreSQL, ensuring a cleaner codebase.
- Bumped version to 2.23.7 to reflect changes.
This commit is contained in:
Giancarlo Buomprisco
2026-01-18 10:44:40 +01:00
committed by GitHub
parent bebd56238b
commit cfa137795b
61 changed files with 3636 additions and 9522 deletions

View File

@@ -1,265 +1,73 @@
# Supabase Database Schema Management
This file contains guidance for working with database schemas, migrations, and Supabase development workflows.
# Supabase Database
## Schema Organization
Schemas are organized in numbered files in the `schemas/` directory. Numbers are used to sort dependencies.
Schemas in `schemas/` directory with numbered prefixes for dependency ordering.
Migrations are generated from schemas. If creating a new schema, the migration can be created using the exact same content.
## Skills
If modifying an existing migration, use the `diff` command:
For database implementation:
- `/postgres-expert` - Schema design, RLS, migrations, testing
### 1. Creating new entities
## Migration Workflow
When creating new entities (such as creating a new tabble), we can create a migration as is, just copying its content.
### New Entities
```bash
# Create new schema file
touch apps/web/supabase/schemas/15-my-new-feature.sql
# Create schema file
touch schemas/20-feature.sql
# Create Migration
pnpm --filter web run supabase migrations new my-new-feature
# Create migration
pnpm --filter web run supabase migrations new feature_name
# Copy content to migration
cp apps/web/supabase/schemas/15-my-new-feature.sql apps/web/supabase/migrations/$(ls -t apps/web/supabase/migrations/ | head -n1)
# Apply migration
pnpm --filter web supabase migrations up # alternatively reset db with pnpm supabase:web:reset
# Generate TypeScript types
# Copy content, apply, generate types
pnpm --filter web supabase migrations up
pnpm supabase:web:typegen
```
### 2. Modifying existing entities
When modifying existing entities (such ass adding a field to an existing table), we can use the `diff` command to generate a migration following the changes:
### Modify Existing
```bash
# Edit schema file (e.g., schemas/03-accounts.sql)
# Make your changes...
# Edit schema, generate diff
pnpm --filter web run supabase:db:diff -f update_feature
# Create migration for changes
pnpm --filter web run supabase:db:diff -f update-accounts
# Apply and test
pnpm --filter web supabase migrations up # alternatively reset db with pnpm supabase:web:reset
# After resetting
# Apply and regenerate
pnpm --filter web supabase migrations up
pnpm supabase:web:typegen
```
## Security First Patterns
## Security Rules
## Add permissions (if any)
- **ALWAYS enable RLS** on new tables
- **NEVER use SECURITY DEFINER** without explicit access controls
- Use existing helper functions (see `/postgres-expert` skill)
## Table Template
```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 (
create table if not exists public.feature (
id uuid unique not null default extensions.uuid_generate_v4(),
account_id uuid references public.accounts(id) on delete cascade not null,
-- ...
created_at timestamp with time zone default now(),
primary key (id)
);
-- CRITICAL: Always enable RLS
alter table "public"."notes" enable row level security;
alter table "public"."feature" enable row level security;
revoke all on public.feature from authenticated, service_role;
grant select, insert, update, delete on table public.feature to authenticated;
-- 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
-- Use helper functions for policies
create policy "feature_read" on public.feature 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
## Commands
```bash
# Test with fresh database
pnpm supabase:web:reset
# Test your changes
pnpm run supabase:web:test
```
## 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 migrations list
# Reset database completely
pnpm supabase:web:reset
# Generate migration from schema diff
pnpm --filter web run supabase:db:diff -f migration-name
## Apply created migration
pnpm --filter web supabase migrations up
# Apply specific migration
pnpm --filter web supabase migrations up --include-schemas public
pnpm supabase:web:reset # Reset database
pnpm supabase:web:typegen # Generate TypeScript types
pnpm --filter web supabase migrations list # View migrations
```