You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Audit logging for all data access and modifications
Role-based access control (RBAC) with principle of least privilege
Data backup and recovery procedures
Regular security assessments and monitoring
Data Protection
API keys and OAuth tokens stored with encryption
Personal data encrypted and access-logged
File storage with secure access policies
Environment variables stored securely
Database Schema
Core Tables
users
createtableusers (
-- Mirror auth.users columns
id uuid primary keyreferencesauth.users(id) on delete cascade,
email text unique not null,
phone text unique,
confirmed_at timestamp with time zone,
email_confirmed_at timestamp with time zone,
last_sign_in_at timestamp with time zone,
raw_app_meta_data jsonb,
raw_user_meta_data jsonb,
created_at timestamp with time zone default now(),
updated_at timestamp with time zone default now(),
deleted_at timestamp with time zone,
-- Extended columns
profile jsonb default '{}',
status textnot nullcheck (status in ('invited', 'waitlist', 'active', 'inactive', 'banned', 'archived')) default 'waitlist',
is_super_user boolean default false
);
-- RLS Policiesaltertable users enable row level security;
-- Users can read their own data
create policy "Users can read own data"on users
for select using (auth.uid() = id);
-- Super users can read all data
create policy "Super users can read all data"on users
for select using (
auth.uid() in (
select id from users where is_super_user = true
)
);
-- Function to copy auth user to users tablecreatefunctionsync_auth_user_to_users()
returns trigger as $$
begininsert into users (
id,
email,
phone,
confirmed_at,
email_confirmed_at,
last_sign_in_at,
raw_app_meta_data,
raw_user_meta_data,
created_at,
updated_at
) values (
NEW.id,
NEW.email,
NEW.phone,
NEW.confirmed_at,
NEW.email_confirmed_at,
NEW.last_sign_in_at,
NEW.raw_app_meta_data,
NEW.raw_user_meta_data,
NEW.created_at,
NEW.updated_at
);
return NEW;
end;
$$ language plpgsql security definer;
-- Trigger to sync auth user to users tablecreatetriggeron_auth_user_created_sync
after insert onauth.users
for each row
execute function sync_auth_user_to_users();
-- Function to check workspace email domain and add usercreatefunctioncheck_and_add_user_to_workspace()
returns trigger as $$
declare
user_domain text;
matching_workspace record;
begin-- Extract domain from email
user_domain := split_part(NEW.email, '@', 2);
-- Find matching workspace
for matching_workspace inselect*from workspaces
where company_email_domains @> array[user_domain]
and not (settings->>'is_global')::boolean= true
loop
-- Add user to workspaceinsert into workspace_users (
workspace_id,
user_id,
role
) values (
matching_workspace.id,
NEW.id,
'project_creator'
);
-- Update user status to active if matchedupdate users
set status ='active'where id =NEW.id;
return NEW;
end loop;
-- If no matching workspace and no invitation exists, keep as waitlist
if not exists (
select1from workspace_users
where user_id =NEW.id
) then
update users
set status ='waitlist'where id =NEW.id;
end if;
return NEW;
end;
$$ language plpgsql security definer;
-- Trigger to check workspace email domain and add usercreatetriggeron_user_created_check_domain
after insert on users
for each row
execute function check_and_add_user_to_workspace();
-- Function to update user status based on workspace/project membershipcreatefunctionupdate_user_status()
returns trigger as $$
begin-- Update user status to active when added to workspace or projectupdate users
set status ='active'where id =NEW.user_idand status in ('waitlist', 'invited');
return NEW;
end;
$$ language plpgsql security definer;
-- Triggers to update user statuscreatetriggeron_workspace_user_added
after insert on workspace_users
for each row
execute function update_user_status();
createtriggeron_project_user_added
after insert on project_users
for each row
execute function update_user_status();
auth.users (managed by Supabase)
Extended with custom profile data
Links to workspace and project roles
workspaces
createtableworkspaces (
id uuid primary key default uuid_generate_v4(),
created_at timestamp with time zone default now(),
updated_at timestamp with time zone default now(),
name textnot null,
description text,
company_name textnot null,
company_email_domains text[], -- Verified company domains
logo_url text,
color_palette jsonb,
settings jsonb default '{}',
is_active boolean default true,
deleted_at timestamp with time zone
);
-- RLS Policiesaltertable workspaces enable row level security;
-- Anyone can read active workspaces they're a member of
create policy "Read workspaces for members"on workspaces
for select using (
auth.uid() in (
select user_id from workspace_users
where workspace_id =workspaces.id
)
);
-- Only workspace owners can update
create policy "Update workspace for owners"on workspaces
for update using (
auth.uid() in (
select user_id from workspace_users
where workspace_id =workspaces.idand role ='owner'
)
);
workspace_users
createtableworkspace_users (
id uuid primary key default uuid_generate_v4(),
workspace_id uuid references workspaces(id) on delete cascade,
user_id uuid references users(id) on delete cascade,
role textnot nullcheck (role in ('owner', 'admin', 'project_creator')),
created_at timestamp with time zone default now(),
invited_by uuid references users(id),
unique(workspace_id, user_id),
constraint valid_role check (role in ('owner', 'admin', 'project_creator'))
);
-- RLS Policiesaltertable workspace_users enable row level security;
-- Workspace members can read other members
create policy "Read workspace members"on workspace_users
for select using (
auth.uid() in (
select user_id from workspace_users
where workspace_id =workspace_users.workspace_id
)
);
-- Only owners/admins can manage members
create policy "Manage members for admins"on workspace_users
for all using (
auth.uid() in (
select user_id from workspace_users
where workspace_id =workspace_users.workspace_idand role in ('owner', 'admin')
)
);
projects
createtableprojects (
id uuid primary key default uuid_generate_v4(),
workspace_id uuid references workspaces(id) on delete cascade,
created_at timestamp with time zone default now(),
updated_at timestamp with time zone default now(),
name textnot null,
description text,
logo_url text,
color_palette jsonb,
settings jsonb default '{}',
is_active boolean default true,
deleted_at timestamp with time zone
);
-- RLS Policiesaltertable projects enable row level security;
-- Project members can read
create policy "Read projects for members"on projects
for select using (
auth.uid() in (
select user_id from project_users
where project_id =projects.id
)
);
-- Project managers and workspace admins can update
create policy "Update project for managers"on projects
for update using (
auth.uid() in (
select user_id from project_users
where project_id =projects.idand role in ('owner', 'manager')
) orauth.uid() in (
select user_id from workspace_users
where workspace_id =projects.workspace_idand role in ('owner', 'admin')
)
);
project_users
createtableproject_users (
id uuid primary key default uuid_generate_v4(),
project_id uuid references projects(id) on delete cascade,
user_id uuid references users(id) on delete cascade,
role textnot null,
created_at timestamp with time zone default now(),
invited_by uuid references users(id),
unique(project_id, user_id),
constraint valid_role check (
role in ('owner', 'manager', 'contributor', 'viewer')
)
);
-- RLS Policiesaltertable project_users enable row level security;
-- Project members can read other members
create policy "Read project members"on project_users
for select using (
auth.uid() in (
select user_id from project_users
where project_id =project_users.project_id
)
);
-- Only project managers and workspace admins can manage members
create policy "Manage project members"on project_users
for all using (
auth.uid() in (
select user_id from project_users
where project_id =project_users.project_idand role in ('owner', 'manager')
) orauth.uid() in (
select user_id from workspace_users
where workspace_id = (
select workspace_id from projects
where id =project_users.project_id
)
and role in ('owner', 'admin')
)
);
API & Integration Tables
api_connections
createtableapi_connections (
id uuid primary key default uuid_generate_v4(),
workspace_id uuid references workspaces(id) on delete cascade,
project_id uuid references projects(id) on delete cascade,
name textnot null,
description text,
api_type textnot null,
endpoint_url textnot null,
auth_type textnot nullcheck (auth_type in ('bearer', 'basic', 'api_key', 'none')),
auth_token text,
custom_headers jsonb default '{}',
http_method textnot nullcheck (http_method in ('GET', 'POST', 'PUT', 'DELETE', 'PATCH')),
request_body_template jsonb default '{}',
response_path text,
response_schema jsonb default '{}',
credentials jsonb not null,
created_at timestamp with time zone default now(),
updated_at timestamp with time zone default now(),
created_by uuid referencesauth.users(id),
is_active boolean default true,
-- Either workspace_id or project_id must be set, not bothconstraint scope_check check (
(workspace_id is nulland project_id is not null) or
(workspace_id is not nulland project_id is null)
)
);
-- RLS Policiesaltertable api_connections enable row level security;
-- Read access for workspace/project members
create policy "Read API connections"on api_connections
for select using (
(
workspace_id is not nullandauth.uid() in (
select user_id from workspace_users
where workspace_id =api_connections.workspace_id
)
) or (
project_id is not nullandauth.uid() in (
select user_id from project_users
where project_id =api_connections.project_id
)
)
);
-- Only admins can manage API connections
create policy "Manage API connections"on api_connections
for all using (
(
workspace_id is not nullandauth.uid() in (
select user_id from workspace_users
where workspace_id =api_connections.workspace_idand role in ('owner', 'admin')
)
) or (
project_id is not nullandauth.uid() in (
select user_id from project_users
where project_id =api_connections.project_idand role in ('owner', 'manager')
)
)
);
oauth2_connections
createtableoauth2_connections (
id uuid primary key default uuid_generate_v4(),
workspace_id uuid references workspaces(id) on delete cascade,
project_id uuid references projects(id) on delete cascade,
provider textnot null,
provider_config jsonb not null,
client_id textnot null,
client_secret textnot null,
redirect_uri textnot null,
scopes text[] not null,
additional_params jsonb default '{}',
created_at timestamp with time zone default now(),
updated_at timestamp with time zone default now(),
created_by uuid referencesauth.users(id),
is_active boolean default true,
-- Either workspace_id or project_id must be set, not bothconstraint scope_check check (
(workspace_id is nulland project_id is not null) or
(workspace_id is not nulland project_id is null)
)
);
-- RLS Policiesaltertable oauth2_connections enable row level security;
-- Read access for workspace/project members
create policy "Read OAuth2 connections"on oauth2_connections
for select using (
(
workspace_id is not nullandauth.uid() in (
select user_id from workspace_users
where workspace_id =oauth2_connections.workspace_id
)
) or (
project_id is not nullandauth.uid() in (
select user_id from project_users
where project_id =oauth2_connections.project_id
)
)
);
-- Only admins can manage OAuth2 connections
create policy "Manage OAuth2 connections"on oauth2_connections
for all using (
(
workspace_id is not nullandauth.uid() in (
select user_id from workspace_users
where workspace_id =oauth2_connections.workspace_idand role in ('owner', 'admin')
)
) or (
project_id is not nullandauth.uid() in (
select user_id from project_users
where project_id =oauth2_connections.project_idand role in ('owner', 'manager')
)
)
);
oauth2_tokens
createtableoauth2_tokens (
id uuid primary key default uuid_generate_v4(),
connection_id uuid references oauth2_connections(id) on delete cascade,
access_token textnot null,
refresh_token text,
token_type textnot null,
expires_at timestamp with time zone,
scope text,
created_at timestamp with time zone default now(),
updated_at timestamp with time zone default now()
);
-- RLS Policiesaltertable oauth2_tokens enable row level security;
-- Inherit connection access permissions
create policy "Access OAuth2 tokens"on oauth2_tokens
for all using (
exists (
select1from oauth2_connections
where id =oauth2_tokens.connection_idand (
(
workspace_id is not nullandauth.uid() in (
select user_id from workspace_users
where workspace_id =oauth2_connections.workspace_id
)
) or (
project_id is not nullandauth.uid() in (
select user_id from project_users
where project_id =oauth2_connections.project_id
)
)
)
)
);
Chat & Messages Tables
chat_sessions
createtablechat_sessions (
id uuid primary key default uuid_generate_v4(),
project_id uuid references projects(id) on delete cascade,
title textnot null,
description text,
settings jsonb default '{}',
is_active boolean default true,
created_at timestamp with time zone default now(),
created_by uuid referencesauth.users(id),
updated_at timestamp with time zone default now(),
updated_by uuid referencesauth.users(id)
);
-- RLS Policiesaltertable chat_sessions enable row level security;
-- Project members can read chat sessions
create policy "Read chat sessions"on chat_sessions
for select using (
auth.uid() in (
select user_id from project_users
where project_id =chat_sessions.project_id
)
);
-- Project contributors and up can manage chat sessions
create policy "Manage chat sessions"on chat_sessions
for all using (
auth.uid() in (
select user_id from project_users
where project_id =chat_sessions.project_idand role in ('owner', 'manager', 'contributor')
)
);
chat_messages
createtablechat_messages (
id uuid primary key default uuid_generate_v4(),
chat_id uuid references chat_sessions(id) on delete cascade,
content jsonb not null,
message_type textnot nullcheck (message_type in ('user', 'ai', 'system')),
agent_type textcheck (agent_type in ('orchestrator', 'requirements', 'architect', 'designer', 'coder', 'quality', 'search', 'scanning', 'data-validation')),
metadata jsonb default '{}',
created_at timestamp with time zone default now(),
created_by uuid referencesauth.users(id),
updated_at timestamp with time zone default now()
);
-- RLS Policiesaltertable chat_messages enable row level security;
-- Inherit chat session access
create policy "Access chat messages"on chat_messages
for all using (
exists (
select1from chat_sessions
where id =chat_messages.chat_idandauth.uid() in (
select user_id from project_users
where project_id =chat_sessions.project_id
)
)
);
chat_participants
createtablechat_participants (
id uuid primary key default uuid_generate_v4(),
chat_id uuid references chat_sessions(id) on delete cascade,
user_id uuid referencesauth.users(id) on delete cascade,
joined_at timestamp with time zone default now(),
last_read_at timestamp with time zone default now(),
is_active boolean default true,
unique(chat_id, user_id)
);
-- RLS Policiesaltertable chat_participants enable row level security;
-- Users can read participant info for their chats
create policy "Read chat participants"on chat_participants
for select using (
exists (
select1from chat_sessions
where id =chat_participants.chat_idandauth.uid() in (
select user_id from project_users
where project_id =chat_sessions.project_id
)
)
);
-- Users can manage their own participant status
create policy "Manage own participant status"on chat_participants
for all using (
auth.uid() = user_id
);
Artifacts & Documents
artifacts
createtableartifacts (
id uuid primary key default uuid_generate_v4(),
project_id uuid references projects(id) on delete cascade,
chat_id uuid references chat_sessions(id),
title textnot null,
description text,
artifact_type textnot null,
status textnot null default 'pending',
progress integer default 0,
created_at timestamp with time zone default now(),
created_by uuid referencesauth.users(id),
updated_at timestamp with time zone default now(),
metadata jsonb default '{}',
is_public boolean default false
);
-- RLS Policiesaltertable artifacts enable row level security;
-- Project members can read artifacts
create policy "Read artifacts"on artifacts
for select using (
auth.uid() in (
select user_id from project_users
where project_id =artifacts.project_id
) or
(is_public = true)
);
-- Project contributors and up can create/update artifacts
create policy "Manage artifacts"on artifacts
for all using (
auth.uid() in (
select user_id from project_users
where project_id =artifacts.project_idand role in ('owner', 'manager', 'contributor')
)
);
artifact_files
createtableartifact_files (
id uuid primary key default uuid_generate_v4(),
artifact_id uuid references artifacts(id) on delete cascade,
file_name textnot null,
file_type textnot null,
file_size integernot null,
file_path textnot null, -- Path for building/rendering the app
is_main_file boolean default false, -- Flag for main file in artifact
storage_path textnot null,
created_at timestamp with time zone default now(),
created_by uuid referencesauth.users(id),
updated_at timestamp with time zone default now(),
updated_by uuid referencesauth.users(id),
metadata jsonb default '{}'
);
-- RLS Policiesaltertable artifact_files enable row level security;
-- Inherit artifact access permissions
create policy "Access artifact files"on artifact_files
for all using (
exists (
select1from artifacts
where id =artifact_files.artifact_idand (
auth.uid() in (
select user_id from project_users
where project_id =artifacts.project_id
) orartifacts.is_public= true
)
)
);
-- Add constraint to ensure only one main file per artifactaltertable artifact_files
add constraint one_main_file_per_artifact
unique (artifact_id, is_main_file)
where is_main_file = true;
Storage Buckets
-- Create secure storage bucketsinsert intostorage.buckets (id, name, public) values
('workspace-files', 'workspace-files', false),
('project-files', 'project-files', false),
('artifact-files', 'artifact-files', false);
-- Storage policies for workspace files
create policy "Workspace file access"onstorage.objects
for all using (
bucket_id ='workspace-files'andauth.uid() in (
select user_id from workspace_users
where workspace_id = ( -- Extract workspace_id from path
(storage.foldername(name)):uuid
)
)
);
-- Storage policies for project files
create policy "Project file access"onstorage.objects
for all using (
bucket_id ='project-files'andauth.uid() in (
select user_id from project_users
where project_id = ( -- Extract project_id from path
(storage.foldername(name)):uuid
)
)
);
-- Storage policies for artifact files
create policy "Artifact file access"onstorage.objects
for all using (
bucket_id ='artifact-files'and
exists (
select1from artifacts
where id = ( -- Extract artifact_id from path
(storage.foldername(name)):uuid
)
and (
auth.uid() in (
select user_id from project_users
where project_id =artifacts.project_id
) orartifacts.is_public= true
)
)
);
Indexes
-- Performance indexescreateindexworkspace_users_user_id_idxon workspace_users(user_id);
createindexproject_users_user_id_idxon project_users(user_id);
createindexchat_messages_chat_id_created_at_idxon chat_messages(chat_id, created_at);
createindexartifacts_project_id_idxon artifacts(project_id);
createindexartifact_files_artifact_id_idxon artifact_files(artifact_id);
-- Search indexescreateindexworkspaces_name_idxon workspaces using gin(to_tsvector('english', name));
createindexprojects_name_idxon projects using gin(to_tsvector('english', name));
createindexartifacts_title_idxon artifacts using gin(to_tsvector('english', title));
Functions & Triggers
-- Update timestamp triggercreatefunctionupdate_updated_at()
returns trigger as $$
beginnew.updated_at= now();
return new;
end;
$$ language plpgsql;
-- Apply to relevant tablescreatetriggerupdate_workspaces_updated_at
before updateon workspaces
for each row execute function update_updated_at();
-- (Add similar triggers for other tables)-- Audit logging functioncreatefunctionaudit_log_changes()
returns trigger as $$
begininsert into audit_logs (
table_name,
record_id,
action,
old_data,
new_data,
changed_by
) values (
TG_TABLE_NAME,
case
when TG_OP ='DELETE' then old.id
else new.id
end,
TG_OP,
case when TG_OP ='INSERT' then null else row_to_json(old) end,
case when TG_OP ='DELETE' then null else row_to_json(new) end,
auth.uid()
);
return null;
end;
$$ language plpgsql;
-- Apply audit logging to sensitive tablescreatetriggeraudit_workspaces
after insert orupdateordeleteon workspaces
for each row execute function audit_log_changes();
-- (Add similar triggers for other sensitive tables)
Security Functions
-- Encrypt sensitive datacreatefunctionencrypt_sensitive_data(data jsonb)
returns textas $$
begin-- Implementation using pgcrypto
return encode(
encrypt(
data::text::bytea,
current_setting('app.encryption_key'),
'aes'
),
'base64'
);
end;
$$ language plpgsql security definer;
-- Decrypt sensitive datacreatefunctiondecrypt_sensitive_data(encrypted_data text)
returns jsonb as $$
begin
return decrypt(
decode(encrypted_data, 'base64'),
current_setting('app.encryption_key'),
'aes'
)::text::jsonb;
end;
$$ language plpgsql security definer;
Backup & Recovery
-- Create backup functioncreatefunctioncreate_backup()
returns void as $$
begin-- Implementation for scheduled backups
end;
$$ language plpgsql;
-- Schedule daily backupsselectcron.schedule(
'daily-backup',
'0 0 ',
'select create_backup()'
);
Billing & Usage Tables
workspace_subscriptions
createtableworkspace_subscriptions (
id uuid primary key default uuid_generate_v4(),
workspace_id uuid references workspaces(id) on delete cascade,
stripe_customer_id textnot null,
stripe_subscription_id textnot null,
plan_type textnot nullcheck (plan_type in ('free', 'hobby', 'pro', 'enterprise')),
base_token_limit bigintnot null,
additional_tokens bigint default 0,
status textnot nullcheck (status in ('active', 'past_due', 'canceled', 'incomplete')),
current_period_start timestamp with time zone,
current_period_end timestamp with time zone,
created_at timestamp with time zone default now(),
updated_at timestamp with time zone default now()
);
-- RLS Policiesaltertable workspace_subscriptions enable row level security;
-- Only workspace owners and admins can read subscription data
create policy "Read subscription data"on workspace_subscriptions
for select using (
auth.uid() in (
select user_id from workspace_users
where workspace_id =workspace_subscriptions.workspace_idand role in ('owner', 'admin')
)
);
token_purchases
createtabletoken_purchases (
id uuid primary key default uuid_generate_v4(),
workspace_id uuid references workspaces(id) on delete cascade,
stripe_payment_intent_id textnot null,
token_amount bigintnot null,
price_paid decimal(10,2) not null,
status textnot nullcheck (status in ('pending', 'completed', 'failed')),
created_at timestamp with time zone default now(),
created_by uuid referencesauth.users(id)
);
-- RLS Policiesaltertable token_purchases enable row level security;
-- Workspace owners and admins can read token purchases
create policy "Read token purchases"on token_purchases
for select using (
auth.uid() in (
select user_id from workspace_users
where workspace_id =token_purchases.workspace_idand role in ('owner', 'admin')
)
);
token_usage
createtabletoken_usage (
id uuid primary key default uuid_generate_v4(),
workspace_id uuid references workspaces(id) on delete cascade,
project_id uuid references projects(id) on delete cascade,
chat_id uuid references chat_sessions(id) on delete cascade,
tokens_used bigintnot null,
usage_type textnot nullcheck (usage_type in ('chat', 'artifact_generation', 'search')),
created_at timestamp with time zone default now(),
created_by uuid referencesauth.users(id)
);
-- RLS Policiesaltertable token_usage enable row level security;
-- Users can read token usage for workspaces/projects they have access to
create policy "Read token usage"on token_usage
for select using (
auth.uid() in (
select user_id from workspace_users
where workspace_id =token_usage.workspace_id
) orauth.uid() in (
select user_id from project_users
where project_id =token_usage.project_id
)
);
Statistics Tables
workspace_stats
createtableworkspace_stats (
id uuid primary key default uuid_generate_v4(),
workspace_id uuid references workspaces(id) on delete cascade,
total_projects integernot null default 0,
total_users integernot null default 0,
total_chats integernot null default 0,
total_artifacts integernot null default 0,
total_files integernot null default 0,
total_tokens_used bigintnot null default 0,
tokens_remaining bigintnot null default 0,
stats_date datenot null default current_date,
created_at timestamp with time zone default now()
);
-- RLS Policiesaltertable workspace_stats enable row level security;
-- Workspace members can read stats
create policy "Read workspace stats"on workspace_stats
for select using (
auth.uid() in (
select user_id from workspace_users
where workspace_id =workspace_stats.workspace_id
)
);
project_stats
createtableproject_stats (
id uuid primary key default uuid_generate_v4(),
project_id uuid references projects(id) on delete cascade,
workspace_id uuid references workspaces(id) on delete cascade,
total_users integernot null default 0,
total_chats integernot null default 0,
total_artifacts integernot null default 0,
total_files integernot null default 0,
total_tokens_used bigintnot null default 0,
tokens_remaining bigintnot null default 0,
stats_date datenot null default current_date,
created_at timestamp with time zone default now()
);
-- RLS Policiesaltertable project_stats enable row level security;
-- Project members can read stats
create policy "Read project stats"on project_stats
for select using (
auth.uid() in (
select user_id from project_users
where project_id =project_stats.project_id
)
);
Audit Logging Tables
audit_logs
createtableaudit_logs (
id uuid primary key default uuid_generate_v4(),
workspace_id uuid references workspaces(id) on delete cascade,
project_id uuid references projects(id) on delete cascade,
table_name textnot null,
record_id uuid not null,
action textnot nullcheck (action in ('INSERT', 'UPDATE', 'DELETE')),
old_data jsonb,
new_data jsonb,
changed_by uuid referencesauth.users(id),
ip_address text,
user_agent text,
created_at timestamp with time zone default now()
);
-- RLS Policiesaltertable audit_logs enable row level security;
-- Workspace owners and admins can read audit logs
create policy "Read workspace audit logs"on audit_logs
for select using (
auth.uid() in (
select user_id from workspace_users
where workspace_id =audit_logs.workspace_idand role in ('owner', 'admin')
)
);
-- Project managers can read project audit logs
create policy "Read project audit logs"on audit_logs
for select using (
auth.uid() in (
select user_id from project_users
where project_id =audit_logs.project_idand role in ('owner', 'manager')
)
);
-- Add audit logging triggers to relevant tablescreatetriggeraudit_workspace_subscriptions
after insert orupdateordeleteon workspace_subscriptions
for each row execute function audit_log_changes();
createtriggeraudit_token_purchases
after insert orupdateordeleteon token_purchases
for each row execute function audit_log_changes();
createtriggeraudit_token_usage
after insert orupdateordeleteon token_usage
for each row execute function audit_log_changes();
-- Add audit logging triggercreatetriggeraudit_api_connections
after insert orupdateordeleteon api_connections
for each row execute function audit_log_changes();
Global Workspace & Personal Project Functions
-- Function to create the default global workspacecreatefunctioncreate_default_global_workspace()
returns uuid as $$
declare
workspace_id uuid;
begininsert into workspaces (
name,
description,
company_name,
company_email_domains,
settings,
is_active
) values (
'Global Workspace',
'Default global workspace for all users',
'System',
array['*'], -- Accepts all email domains
jsonb_build_object(
'is_global', true,
'managed_by_super_user', true
),
true
) returning id into workspace_id;
-- Create free subscription for global workspaceinsert into workspace_subscriptions (
workspace_id,
stripe_customer_id,
stripe_subscription_id,
plan_type,
base_token_limit,
status
) values (
workspace_id,
'global_workspace',
'global_subscription',
'free',
1000000, -- 1 million tokens'active'
);
return workspace_id;
end;
$$ language plpgsql security definer;
-- Function to add user to global workspacecreatefunctionadd_user_to_global_workspace()
returns trigger as $$
declare
global_workspace_id uuid;
begin-- Get the global workspace IDselect id into global_workspace_id
from workspaces
where (settings->>'is_global')::boolean= true
limit1;
-- Add user to global workspaceinsert into workspace_users (
workspace_id,
user_id,
role
) values (
global_workspace_id,
NEW.id,
'project_creator'
);
-- Create personal project for the userinsert into projects (
workspace_id,
name,
description,
settings
) values (
global_workspace_id,
NEW.email||'''s Personal Project',
'Personal project space',
jsonb_build_object(
'is_personal', true,
'owner_id', NEW.id
)
);
return NEW;
end;
$$ language plpgsql security definer;
-- Trigger to add new users to global workspace and create personal projectcreatetriggeron_auth_user_created
after insert onauth.users
for each row
execute function add_user_to_global_workspace();
-- Function to archive personal project on user deletioncreatefunctionarchive_personal_project()
returns trigger as $$
begin-- Archive the personal project instead of deletingupdate projects
set
is_active = false,
deleted_at = now(),
settings = settings || jsonb_build_object('archived_reason', 'user_deleted')
where
workspace_id = (
select id from workspaces
where (settings->>'is_global')::boolean= true
limit1
)
and (settings->>'is_personal')::boolean= true
and (settings->>'owner_id')::uuid =OLD.id;
return OLD;
end;
$$ language plpgsql security definer;
-- Trigger to archive personal project when user is deletedcreatetriggeron_auth_user_deleted
before deleteonauth.users
for each row
execute function archive_personal_project();
-- Modify projects table RLS policies to handle personal projects
create policy "Read personal project"on projects
for select
using (
auth.uid() = (settings->>'owner_id')::uuid
and (settings->>'is_personal')::boolean= true
);
create policy "Manage personal project"on projects
for all
using (
auth.uid() = (settings->>'owner_id')::uuid
and (settings->>'is_personal')::boolean= true
);
-- Initialize global workspace on database creation
do $$
begin
perform create_default_global_workspace();
end $$;
Project Context Tables
project_requirements
createtableproject_requirements (
id uuid primary key default uuid_generate_v4(),
project_id uuid references projects(id) on delete cascade,
title textnot null,
content jsonb not null,
version integernot null default 1,
status textnot nullcheck (status in ('draft', 'review', 'approved', 'archived')),
created_at timestamp with time zone default now(),
created_by uuid referencesauth.users(id),
updated_at timestamp with time zone default now(),
updated_by uuid referencesauth.users(id)
);
-- RLS Policiesaltertable project_requirements enable row level security;
-- Project members can read requirements
create policy "Read project requirements"on project_requirements
for select using (
auth.uid() in (
select user_id from project_users
where project_id =project_requirements.project_id
)
);
-- Project managers and up can manage requirements
create policy "Manage project requirements"on project_requirements
for all using (
auth.uid() in (
select user_id from project_users
where project_id =project_requirements.project_idand role in ('owner', 'manager')
)
);
technical_architecture
createtabletechnical_architecture (
id uuid primary key default uuid_generate_v4(),
project_id uuid references projects(id) on delete cascade,
title textnot null,
content jsonb not null,
version integernot null default 1,
status textnot nullcheck (status in ('draft', 'review', 'approved', 'archived')),
apis_used jsonb default '[]',
data_schemas jsonb default '[]',
security_requirements jsonb default '[]',
created_at timestamp with time zone default now(),
created_by uuid referencesauth.users(id),
updated_at timestamp with time zone default now(),
updated_by uuid referencesauth.users(id)
);
-- RLS Policiesaltertable technical_architecture enable row level security;
-- Project members can read architecture
create policy "Read technical architecture"on technical_architecture
for select using (
auth.uid() in (
select user_id from project_users
where project_id =technical_architecture.project_id
)
);
-- Project managers and up can manage architecture
create policy "Manage technical architecture"on technical_architecture
for all using (
auth.uid() in (
select user_id from project_users
where project_id =technical_architecture.project_idand role in ('owner', 'manager')
)
);
ui_design
createtableui_design (
id uuid primary key default uuid_generate_v4(),
project_id uuid references projects(id) on delete cascade,
title textnot null,
content jsonb not null,
version integernot null default 1,
status textnot nullcheck (status in ('draft', 'review', 'approved', 'archived')),
color_palette jsonb,
ui_framework text,
components jsonb default '[]',
created_at timestamp with time zone default now(),
created_by uuid referencesauth.users(id),
updated_at timestamp with time zone default now(),
updated_by uuid referencesauth.users(id)
);
-- RLS Policiesaltertable ui_design enable row level security;
-- Project members can read UI design
create policy "Read UI design"on ui_design
for select using (
auth.uid() in (
select user_id from project_users
where project_id =ui_design.project_id
)
);
-- Project managers and up can manage UI design
create policy "Manage UI design"on ui_design
for all using (
auth.uid() in (
select user_id from project_users
where project_id =ui_design.project_idand role in ('owner', 'manager')
)
);
project_context
createtableproject_context (
id uuid primary key default uuid_generate_v4(),
project_id uuid references projects(id) on delete cascade,
context_type textnot nullcheck (context_type in ('search', 'data', 'quality')),
title textnot null,
content jsonb not null,
metadata jsonb default '{}',
created_at timestamp with time zone default now(),
created_by uuid referencesauth.users(id),
updated_at timestamp with time zone default now(),
updated_by uuid referencesauth.users(id)
);
-- RLS Policiesaltertable project_context enable row level security;
-- Project members can read context
create policy "Read project context"on project_context
for select using (
auth.uid() in (
select user_id from project_users
where project_id =project_context.project_id
)
);
-- Project managers and up can manage context
create policy "Manage project context"on project_context
for all using (
auth.uid() in (
select user_id from project_users
where project_id =project_context.project_idand role in ('owner', 'manager')
)
);