From af908ae685f10d0beb59163d5c43987e9f2bec70 Mon Sep 17 00:00:00 2001 From: giancarlo Date: Fri, 29 Mar 2024 17:39:35 +0800 Subject: [PATCH] Refactor team accounts feature and improve form validation Updated several components within the team accounts feature to use more specific schema definitions and provide clearer form validation. Several schema files were renamed to better reflect their usage and additional properties were included. The commit also introduces handling for a new accountId property, which provides more accurate user account tracking. Autocomplete was turned off on deletion actions for better security. Changes related to account ownership transfer were also made, including transaction logging and error handling improvements. --- .../home/[account]/members/page.tsx | 1 + .../account-danger-zone.tsx | 1 + .../invitations/update-invitation-dialog.tsx | 4 +- .../members/account-members-table.tsx | 23 +- .../members/transfer-ownership-dialog.tsx | 40 +- .../members/update-member-role-dialog.tsx | 4 +- .../settings/team-account-danger-zone.tsx | 2 + .../src/schema/delete-team-account.schema.ts | 2 +- .../src/schema/invite-members.schema.ts | 4 +- .../src/schema/remove-member.schema.ts | 6 + .../transfer-ownership-confirmation.schema.ts | 13 +- .../src/schema/update-member-role-schema.ts | 10 + .../src/schema/update-role-schema.ts | 5 - .../actions/team-members-server-actions.ts | 64 +- .../services/account-members.service.ts | 50 +- packages/supabase/src/database.types.ts | 1622 +++++++++-------- supabase/migrations/20221215192558_schema.sql | 31 + 17 files changed, 995 insertions(+), 887 deletions(-) create mode 100644 packages/features/team-accounts/src/schema/remove-member.schema.ts create mode 100644 packages/features/team-accounts/src/schema/update-member-role-schema.ts delete mode 100644 packages/features/team-accounts/src/schema/update-role-schema.ts diff --git a/apps/web/app/(dashboard)/home/[account]/members/page.tsx b/apps/web/app/(dashboard)/home/[account]/members/page.tsx index 564279558..1c0c06383 100644 --- a/apps/web/app/(dashboard)/home/[account]/members/page.tsx +++ b/apps/web/app/(dashboard)/home/[account]/members/page.tsx @@ -147,6 +147,7 @@ async function TeamAccountMembersPage({ params }: Params) { { return data.role !== userRole; }, diff --git a/packages/features/team-accounts/src/components/members/account-members-table.tsx b/packages/features/team-accounts/src/components/members/account-members-table.tsx index bce170118..06ad00da8 100644 --- a/packages/features/team-accounts/src/components/members/account-members-table.tsx +++ b/packages/features/team-accounts/src/components/members/account-members-table.tsx @@ -37,6 +37,7 @@ interface Permissions { type AccountMembersTableProps = { members: Members; currentUserId: string; + currentAccountId: string; userRoleHierarchy: number; isPrimaryOwner: boolean; canManageRoles: boolean; @@ -45,6 +46,7 @@ type AccountMembersTableProps = { export function AccountMembersTable({ members, currentUserId, + currentAccountId, isPrimaryOwner, userRoleHierarchy, canManageRoles, @@ -60,7 +62,10 @@ export function AccountMembersTable({ canTransferOwnership: isPrimaryOwner, }; - const columns = useGetColumns(permissions, currentUserId); + const columns = useGetColumns(permissions, { + currentUserId, + currentAccountId, + }); const filteredMembers = members.filter((member) => { const searchString = search.toLowerCase(); @@ -87,7 +92,10 @@ export function AccountMembersTable({ function useGetColumns( permissions: Permissions, - currentUserId: string, + params: { + currentUserId: string; + currentAccountId: string; + }, ): ColumnDef[] { const { t } = useTranslation('teams'); @@ -99,7 +107,7 @@ function useGetColumns( cell: ({ row }) => { const member = row.original; const displayName = member.name ?? member.email.split('@')[0]; - const isSelf = member.user_id === currentUserId; + const isSelf = member.user_id === params.currentUserId; return ( @@ -168,12 +176,13 @@ function useGetColumns( ), }, ], - [permissions, currentUserId, t], + [t, params, permissions], ); } @@ -181,10 +190,12 @@ function ActionsDropdown({ permissions, member, currentUserId, + accountId, }: { permissions: Permissions; member: Members[0]; currentUserId: string; + accountId: string; }) { const [isRemoving, setIsRemoving] = useState(false); const [isTransferring, setIsTransferring] = useState(false); @@ -268,7 +279,7 @@ function ActionsDropdown({ isOpen setIsOpen={setIsTransferring} targetDisplayName={member.name ?? member.email} - accountId={member.id} + accountId={accountId} userId={member.user_id} /> diff --git a/packages/features/team-accounts/src/components/members/transfer-ownership-dialog.tsx b/packages/features/team-accounts/src/components/members/transfer-ownership-dialog.tsx index d3e76a96d..8aca718ff 100644 --- a/packages/features/team-accounts/src/components/members/transfer-ownership-dialog.tsx +++ b/packages/features/team-accounts/src/components/members/transfer-ownership-dialog.tsx @@ -77,25 +77,12 @@ function TransferOrganizationOwnershipForm({ const [pending, startTransition] = useTransition(); const [error, setError] = useState(); - const onSubmit = () => { - startTransition(async () => { - try { - await transferOwnershipAction({ - accountId, - userId, - }); - - setIsOpen(false); - } catch (error) { - setError(true); - } - }); - }; - const form = useForm({ resolver: zodResolver(TransferOwnershipConfirmationSchema), defaultValues: { confirmation: '', + accountId, + userId, }, }); @@ -103,7 +90,17 @@ function TransferOrganizationOwnershipForm({
{ + startTransition(async () => { + try { + await transferOwnershipAction(data); + + setIsOpen(false); + } catch (error) { + setError(true); + } + }); + })} > @@ -129,7 +126,12 @@ function TransferOrganizationOwnershipForm({ - + @@ -176,11 +178,11 @@ function TransferOwnershipErrorAlert() { return ( - + - + ); diff --git a/packages/features/team-accounts/src/components/members/update-member-role-dialog.tsx b/packages/features/team-accounts/src/components/members/update-member-role-dialog.tsx index 335a9fba4..bc7b90a6a 100644 --- a/packages/features/team-accounts/src/components/members/update-member-role-dialog.tsx +++ b/packages/features/team-accounts/src/components/members/update-member-role-dialog.tsx @@ -25,7 +25,7 @@ import { import { If } from '@kit/ui/if'; import { Trans } from '@kit/ui/trans'; -import { UpdateRoleSchema } from '../../schema/update-role-schema'; +import { RoleSchema } from '../../schema/update-member-role-schema'; import { updateMemberRoleAction } from '../../server/actions/team-members-server-actions'; import { MembershipRoleSelector } from './membership-role-selector'; import { RolesDataProvider } from './roles-data-provider'; @@ -107,7 +107,7 @@ function UpdateMemberForm({ const form = useForm({ resolver: zodResolver( - UpdateRoleSchema.refine( + RoleSchema.refine( (data) => { return data.role !== userRole; }, diff --git a/packages/features/team-accounts/src/components/settings/team-account-danger-zone.tsx b/packages/features/team-accounts/src/components/settings/team-account-danger-zone.tsx index 06070802b..149ea4624 100644 --- a/packages/features/team-accounts/src/components/settings/team-account-danger-zone.tsx +++ b/packages/features/team-accounts/src/components/settings/team-account-danger-zone.tsx @@ -189,6 +189,7 @@ function DeleteTeamConfirmationForm({ data-test={'delete-team-input-field'} required type={'text'} + autoComplete={'off'} className={'w-full'} placeholder={''} pattern={name} @@ -315,6 +316,7 @@ function LeaveTeamContainer(props: { data-test="leave-team-input-field" type="text" className="w-full" + autoComplete={'off'} placeholder="" pattern="LEAVE" required diff --git a/packages/features/team-accounts/src/schema/delete-team-account.schema.ts b/packages/features/team-accounts/src/schema/delete-team-account.schema.ts index bc182edbb..a52606041 100644 --- a/packages/features/team-accounts/src/schema/delete-team-account.schema.ts +++ b/packages/features/team-accounts/src/schema/delete-team-account.schema.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; export const DeleteTeamAccountSchema = z.object({ - accountId: z.string(), + accountId: z.string().uuid(), }); diff --git a/packages/features/team-accounts/src/schema/invite-members.schema.ts b/packages/features/team-accounts/src/schema/invite-members.schema.ts index 772265091..9ae6cc240 100644 --- a/packages/features/team-accounts/src/schema/invite-members.schema.ts +++ b/packages/features/team-accounts/src/schema/invite-members.schema.ts @@ -1,10 +1,8 @@ import { z } from 'zod'; -type Role = string; - const InviteSchema = z.object({ email: z.string().email(), - role: z.custom(() => z.string().min(1)), + role: z.string().min(1), }); export const InviteMembersSchema = z diff --git a/packages/features/team-accounts/src/schema/remove-member.schema.ts b/packages/features/team-accounts/src/schema/remove-member.schema.ts new file mode 100644 index 000000000..b693d33c9 --- /dev/null +++ b/packages/features/team-accounts/src/schema/remove-member.schema.ts @@ -0,0 +1,6 @@ +import { z } from 'zod'; + +export const RemoveMemberSchema = z.object({ + accountId: z.string().uuid(), + userId: z.string().uuid(), +}); diff --git a/packages/features/team-accounts/src/schema/transfer-ownership-confirmation.schema.ts b/packages/features/team-accounts/src/schema/transfer-ownership-confirmation.schema.ts index c8918937e..9d6a152e3 100644 --- a/packages/features/team-accounts/src/schema/transfer-ownership-confirmation.schema.ts +++ b/packages/features/team-accounts/src/schema/transfer-ownership-confirmation.schema.ts @@ -2,11 +2,8 @@ import { z } from 'zod'; const confirmationString = 'TRANSFER'; -export const TransferOwnershipConfirmationSchema = z - .object({ - confirmation: z.string(), - }) - .refine((data) => data.confirmation === confirmationString, { - message: `Confirmation must be ${confirmationString}`, - path: ['confirmation'], - }); +export const TransferOwnershipConfirmationSchema = z.object({ + userId: z.string().uuid(), + confirmation: z.custom((value) => value === confirmationString), + accountId: z.string().uuid(), +}); diff --git a/packages/features/team-accounts/src/schema/update-member-role-schema.ts b/packages/features/team-accounts/src/schema/update-member-role-schema.ts new file mode 100644 index 000000000..e3975adf6 --- /dev/null +++ b/packages/features/team-accounts/src/schema/update-member-role-schema.ts @@ -0,0 +1,10 @@ +import { z } from 'zod'; + +export const RoleSchema = z.object({ + role: z.string().min(1), +}); + +export const UpdateMemberRoleSchema = RoleSchema.extend({ + accountId: z.string().uuid(), + userId: z.string().uuid(), +}); diff --git a/packages/features/team-accounts/src/schema/update-role-schema.ts b/packages/features/team-accounts/src/schema/update-role-schema.ts deleted file mode 100644 index ab0b727f3..000000000 --- a/packages/features/team-accounts/src/schema/update-role-schema.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { z } from 'zod'; - -export const UpdateRoleSchema = z.object({ - role: z.string().min(1), -}); diff --git a/packages/features/team-accounts/src/server/actions/team-members-server-actions.ts b/packages/features/team-accounts/src/server/actions/team-members-server-actions.ts index 344ab7841..0cba7f97f 100644 --- a/packages/features/team-accounts/src/server/actions/team-members-server-actions.ts +++ b/packages/features/team-accounts/src/server/actions/team-members-server-actions.ts @@ -1,16 +1,22 @@ 'use server'; +import { revalidatePath } from 'next/cache'; + import { SupabaseClient } from '@supabase/supabase-js'; +import { z } from 'zod'; + import { Database } from '@kit/supabase/database'; import { getSupabaseServerActionClient } from '@kit/supabase/server-actions-client'; +import { RemoveMemberSchema } from '../../schema/remove-member.schema'; +import { TransferOwnershipConfirmationSchema } from '../../schema/transfer-ownership-confirmation.schema'; +import { UpdateMemberRoleSchema } from '../../schema/update-member-role-schema'; import { AccountMembersService } from '../services/account-members.service'; -export async function removeMemberFromAccountAction(params: { - accountId: string; - userId: string; -}) { +export async function removeMemberFromAccountAction( + params: z.infer, +) { const client = getSupabaseServerActionClient(); const { data, error } = await client.auth.getUser(); @@ -18,21 +24,21 @@ export async function removeMemberFromAccountAction(params: { throw new Error(`Authentication required`); } + const { accountId, userId } = RemoveMemberSchema.parse(params); + const service = new AccountMembersService(client); await service.removeMemberFromAccount({ - accountId: params.accountId, - userId: params.userId, + accountId, + userId, }); return { success: true }; } -export async function updateMemberRoleAction(params: { - accountId: string; - userId: string; - role: string; -}) { +export async function updateMemberRoleAction( + params: z.infer, +) { const client = getSupabaseServerActionClient(); await assertSession(client); @@ -48,21 +54,43 @@ export async function updateMemberRoleAction(params: { return { success: true }; } -export async function transferOwnershipAction(params: { - accountId: string; - userId: string; -}) { +export async function transferOwnershipAction( + params: z.infer, +) { const client = getSupabaseServerActionClient(); + const { accountId, userId } = + TransferOwnershipConfirmationSchema.parse(params); + + // assert that the user is authenticated await assertSession(client); - const service = new AccountMembersService(client); + // assert that the user is the owner of the account + const { data: isOwner, error } = await client.rpc('is_account_owner', { + account_id: accountId, + }); + + if (error ?? !isOwner) { + throw new Error( + `You must be the owner of the account to transfer ownership`, + ); + } + + // at this point, the user is authenticated and is the owner of the account + // so we proceed with the transfer of ownership with admin privileges + const service = new AccountMembersService( + getSupabaseServerActionClient({ admin: true }), + ); await service.transferOwnership({ - accountId: params.accountId, - userId: params.userId, + accountId, + userId, + confirmation: params.confirmation, }); + // revalidate all pages that depend on the account + revalidatePath('/home/[account]', 'layout'); + return { success: true }; } diff --git a/packages/features/team-accounts/src/server/services/account-members.service.ts b/packages/features/team-accounts/src/server/services/account-members.service.ts index d2d789820..dbc06fc9f 100644 --- a/packages/features/team-accounts/src/server/services/account-members.service.ts +++ b/packages/features/team-accounts/src/server/services/account-members.service.ts @@ -1,13 +1,21 @@ import { SupabaseClient } from '@supabase/supabase-js'; import 'server-only'; +import { z } from 'zod'; +import { Logger } from '@kit/shared/logger'; import { Database } from '@kit/supabase/database'; +import { RemoveMemberSchema } from '../../schema/remove-member.schema'; +import { TransferOwnershipConfirmationSchema } from '../../schema/transfer-ownership-confirmation.schema'; +import { UpdateMemberRoleSchema } from '../../schema/update-member-role-schema'; + export class AccountMembersService { + private readonly namespace = 'account-members'; + constructor(private readonly client: SupabaseClient) {} - async removeMemberFromAccount(params: { accountId: string; userId: string }) { + async removeMemberFromAccount(params: z.infer) { const { data, error } = await this.client .from('accounts_memberships') .delete() @@ -23,11 +31,7 @@ export class AccountMembersService { return data; } - async updateMemberRole(params: { - accountId: string; - userId: string; - role: string; - }) { + async updateMemberRole(params: z.infer) { const { data, error } = await this.client .from('accounts_memberships') .update({ @@ -45,21 +49,35 @@ export class AccountMembersService { return data; } - async transferOwnership(params: { accountId: string; userId: string }) { - const { data, error } = await this.client - .from('accounts') - .update({ - primary_owner_user_id: params.userId, - }) - .match({ - id: params.accountId, - user_id: params.userId, - }); + async transferOwnership( + params: z.infer, + ) { + const ctx = { + namespace: this.namespace, + ...params, + }; + + Logger.info(ctx, `Transferring ownership of account`); + + const { data, error } = await this.client.rpc( + 'transfer_team_account_ownership', + { + target_account_id: params.accountId, + new_owner_id: params.userId, + }, + ); if (error) { + Logger.error( + { ...ctx, error }, + `Failed to transfer ownership of account`, + ); + throw error; } + Logger.info(ctx, `Successfully transferred ownership of account`); + return data; } } diff --git a/packages/supabase/src/database.types.ts b/packages/supabase/src/database.types.ts index 29a1b133a..0730ceddf 100644 --- a/packages/supabase/src/database.types.ts +++ b/packages/supabase/src/database.types.ts @@ -4,1062 +4,1070 @@ export type Json = | boolean | null | { [key: string]: Json | undefined } - | Json[]; + | Json[] export type Database = { graphql_public: { Tables: { - [_ in never]: never; - }; + [_ in never]: never + } Views: { - [_ in never]: never; - }; + [_ in never]: never + } Functions: { graphql: { Args: { - operationName?: string; - query?: string; - variables?: Json; - extensions?: Json; - }; - Returns: Json; - }; - }; + operationName?: string + query?: string + variables?: Json + extensions?: Json + } + Returns: Json + } + } Enums: { - [_ in never]: never; - }; + [_ in never]: never + } CompositeTypes: { - [_ in never]: never; - }; - }; + [_ in never]: never + } + } public: { Tables: { account_roles: { Row: { - account_id: string; - id: number; - role: string; - }; + account_id: string + id: number + role: string + } Insert: { - account_id: string; - id?: number; - role: string; - }; + account_id: string + id?: number + role: string + } Update: { - account_id?: string; - id?: number; - role?: string; - }; + account_id?: string + id?: number + role?: string + } Relationships: [ { - foreignKeyName: 'account_roles_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'accounts'; - referencedColumns: ['id']; + foreignKeyName: "account_roles_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "accounts" + referencedColumns: ["id"] }, { - foreignKeyName: 'account_roles_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_account_workspace'; - referencedColumns: ['id']; + foreignKeyName: "account_roles_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_account_workspace" + referencedColumns: ["id"] }, { - foreignKeyName: 'account_roles_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_accounts'; - referencedColumns: ['id']; + foreignKeyName: "account_roles_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_accounts" + referencedColumns: ["id"] }, { - foreignKeyName: 'account_roles_role_fkey'; - columns: ['role']; - isOneToOne: false; - referencedRelation: 'roles'; - referencedColumns: ['name']; + foreignKeyName: "account_roles_role_fkey" + columns: ["role"] + isOneToOne: false + referencedRelation: "roles" + referencedColumns: ["name"] }, - ]; - }; + ] + } accounts: { Row: { - created_at: string | null; - created_by: string | null; - email: string | null; - id: string; - is_personal_account: boolean; - name: string; - picture_url: string | null; - primary_owner_user_id: string; - slug: string | null; - updated_at: string | null; - updated_by: string | null; - }; + created_at: string | null + created_by: string | null + email: string | null + id: string + is_personal_account: boolean + name: string + picture_url: string | null + primary_owner_user_id: string + slug: string | null + updated_at: string | null + updated_by: string | null + } Insert: { - created_at?: string | null; - created_by?: string | null; - email?: string | null; - id?: string; - is_personal_account?: boolean; - name: string; - picture_url?: string | null; - primary_owner_user_id?: string; - slug?: string | null; - updated_at?: string | null; - updated_by?: string | null; - }; + created_at?: string | null + created_by?: string | null + email?: string | null + id?: string + is_personal_account?: boolean + name: string + picture_url?: string | null + primary_owner_user_id?: string + slug?: string | null + updated_at?: string | null + updated_by?: string | null + } Update: { - created_at?: string | null; - created_by?: string | null; - email?: string | null; - id?: string; - is_personal_account?: boolean; - name?: string; - picture_url?: string | null; - primary_owner_user_id?: string; - slug?: string | null; - updated_at?: string | null; - updated_by?: string | null; - }; + created_at?: string | null + created_by?: string | null + email?: string | null + id?: string + is_personal_account?: boolean + name?: string + picture_url?: string | null + primary_owner_user_id?: string + slug?: string | null + updated_at?: string | null + updated_by?: string | null + } Relationships: [ { - foreignKeyName: 'accounts_created_by_fkey'; - columns: ['created_by']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "accounts_created_by_fkey" + columns: ["created_by"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'accounts_primary_owner_user_id_fkey'; - columns: ['primary_owner_user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "accounts_primary_owner_user_id_fkey" + columns: ["primary_owner_user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'accounts_updated_by_fkey'; - columns: ['updated_by']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "accounts_updated_by_fkey" + columns: ["updated_by"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } accounts_memberships: { Row: { - account_id: string; - account_role: string; - created_at: string; - created_by: string | null; - updated_at: string; - updated_by: string | null; - user_id: string; - }; + account_id: string + account_role: string + created_at: string + created_by: string | null + updated_at: string + updated_by: string | null + user_id: string + } Insert: { - account_id: string; - account_role: string; - created_at?: string; - created_by?: string | null; - updated_at?: string; - updated_by?: string | null; - user_id: string; - }; + account_id: string + account_role: string + created_at?: string + created_by?: string | null + updated_at?: string + updated_by?: string | null + user_id: string + } Update: { - account_id?: string; - account_role?: string; - created_at?: string; - created_by?: string | null; - updated_at?: string; - updated_by?: string | null; - user_id?: string; - }; + account_id?: string + account_role?: string + created_at?: string + created_by?: string | null + updated_at?: string + updated_by?: string | null + user_id?: string + } Relationships: [ { - foreignKeyName: 'accounts_memberships_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'accounts'; - referencedColumns: ['id']; + foreignKeyName: "accounts_memberships_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "accounts" + referencedColumns: ["id"] }, { - foreignKeyName: 'accounts_memberships_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_account_workspace'; - referencedColumns: ['id']; + foreignKeyName: "accounts_memberships_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_account_workspace" + referencedColumns: ["id"] }, { - foreignKeyName: 'accounts_memberships_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_accounts'; - referencedColumns: ['id']; + foreignKeyName: "accounts_memberships_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_accounts" + referencedColumns: ["id"] }, { - foreignKeyName: 'accounts_memberships_account_role_fkey'; - columns: ['account_role']; - isOneToOne: false; - referencedRelation: 'roles'; - referencedColumns: ['name']; + foreignKeyName: "accounts_memberships_account_role_fkey" + columns: ["account_role"] + isOneToOne: false + referencedRelation: "roles" + referencedColumns: ["name"] }, { - foreignKeyName: 'accounts_memberships_created_by_fkey'; - columns: ['created_by']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "accounts_memberships_created_by_fkey" + columns: ["created_by"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'accounts_memberships_updated_by_fkey'; - columns: ['updated_by']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "accounts_memberships_updated_by_fkey" + columns: ["updated_by"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'accounts_memberships_user_id_fkey'; - columns: ['user_id']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "accounts_memberships_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, - ]; - }; + ] + } billing_customers: { Row: { - account_id: string; - customer_id: string; - email: string | null; - id: number; - provider: Database['public']['Enums']['billing_provider']; - }; + account_id: string + customer_id: string + email: string | null + id: number + provider: Database["public"]["Enums"]["billing_provider"] + } Insert: { - account_id: string; - customer_id: string; - email?: string | null; - id?: number; - provider: Database['public']['Enums']['billing_provider']; - }; + account_id: string + customer_id: string + email?: string | null + id?: number + provider: Database["public"]["Enums"]["billing_provider"] + } Update: { - account_id?: string; - customer_id?: string; - email?: string | null; - id?: number; - provider?: Database['public']['Enums']['billing_provider']; - }; + account_id?: string + customer_id?: string + email?: string | null + id?: number + provider?: Database["public"]["Enums"]["billing_provider"] + } Relationships: [ { - foreignKeyName: 'billing_customers_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'accounts'; - referencedColumns: ['id']; + foreignKeyName: "billing_customers_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "accounts" + referencedColumns: ["id"] }, { - foreignKeyName: 'billing_customers_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_account_workspace'; - referencedColumns: ['id']; + foreignKeyName: "billing_customers_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_account_workspace" + referencedColumns: ["id"] }, { - foreignKeyName: 'billing_customers_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_accounts'; - referencedColumns: ['id']; + foreignKeyName: "billing_customers_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_accounts" + referencedColumns: ["id"] }, - ]; - }; + ] + } config: { Row: { - billing_provider: Database['public']['Enums']['billing_provider']; - enable_account_billing: boolean; - enable_organization_accounts: boolean; - enable_organization_billing: boolean; - }; + billing_provider: Database["public"]["Enums"]["billing_provider"] + enable_account_billing: boolean + enable_organization_accounts: boolean + enable_organization_billing: boolean + } Insert: { - billing_provider?: Database['public']['Enums']['billing_provider']; - enable_account_billing?: boolean; - enable_organization_accounts?: boolean; - enable_organization_billing?: boolean; - }; + billing_provider?: Database["public"]["Enums"]["billing_provider"] + enable_account_billing?: boolean + enable_organization_accounts?: boolean + enable_organization_billing?: boolean + } Update: { - billing_provider?: Database['public']['Enums']['billing_provider']; - enable_account_billing?: boolean; - enable_organization_accounts?: boolean; - enable_organization_billing?: boolean; - }; - Relationships: []; - }; + billing_provider?: Database["public"]["Enums"]["billing_provider"] + enable_account_billing?: boolean + enable_organization_accounts?: boolean + enable_organization_billing?: boolean + } + Relationships: [] + } invitations: { Row: { - account_id: string; - created_at: string; - email: string; - expires_at: string; - id: number; - invite_token: string; - invited_by: string; - role: string; - updated_at: string; - }; + account_id: string + created_at: string + email: string + expires_at: string + id: number + invite_token: string + invited_by: string + role: string + updated_at: string + } Insert: { - account_id: string; - created_at?: string; - email: string; - expires_at?: string; - id?: number; - invite_token: string; - invited_by: string; - role: string; - updated_at?: string; - }; + account_id: string + created_at?: string + email: string + expires_at?: string + id?: number + invite_token: string + invited_by: string + role: string + updated_at?: string + } Update: { - account_id?: string; - created_at?: string; - email?: string; - expires_at?: string; - id?: number; - invite_token?: string; - invited_by?: string; - role?: string; - updated_at?: string; - }; + account_id?: string + created_at?: string + email?: string + expires_at?: string + id?: number + invite_token?: string + invited_by?: string + role?: string + updated_at?: string + } Relationships: [ { - foreignKeyName: 'invitations_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'accounts'; - referencedColumns: ['id']; + foreignKeyName: "invitations_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "accounts" + referencedColumns: ["id"] }, { - foreignKeyName: 'invitations_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_account_workspace'; - referencedColumns: ['id']; + foreignKeyName: "invitations_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_account_workspace" + referencedColumns: ["id"] }, { - foreignKeyName: 'invitations_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_accounts'; - referencedColumns: ['id']; + foreignKeyName: "invitations_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_accounts" + referencedColumns: ["id"] }, { - foreignKeyName: 'invitations_invited_by_fkey'; - columns: ['invited_by']; - isOneToOne: false; - referencedRelation: 'users'; - referencedColumns: ['id']; + foreignKeyName: "invitations_invited_by_fkey" + columns: ["invited_by"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] }, { - foreignKeyName: 'invitations_role_fkey'; - columns: ['role']; - isOneToOne: false; - referencedRelation: 'roles'; - referencedColumns: ['name']; + foreignKeyName: "invitations_role_fkey" + columns: ["role"] + isOneToOne: false + referencedRelation: "roles" + referencedColumns: ["name"] }, - ]; - }; + ] + } role_permissions: { Row: { - id: number; - permission: Database['public']['Enums']['app_permissions']; - role: string; - }; + id: number + permission: Database["public"]["Enums"]["app_permissions"] + role: string + } Insert: { - id?: number; - permission: Database['public']['Enums']['app_permissions']; - role: string; - }; + id?: number + permission: Database["public"]["Enums"]["app_permissions"] + role: string + } Update: { - id?: number; - permission?: Database['public']['Enums']['app_permissions']; - role?: string; - }; + id?: number + permission?: Database["public"]["Enums"]["app_permissions"] + role?: string + } Relationships: [ { - foreignKeyName: 'role_permissions_role_fkey'; - columns: ['role']; - isOneToOne: false; - referencedRelation: 'roles'; - referencedColumns: ['name']; + foreignKeyName: "role_permissions_role_fkey" + columns: ["role"] + isOneToOne: false + referencedRelation: "roles" + referencedColumns: ["name"] }, - ]; - }; + ] + } roles: { Row: { - account_id: string | null; - hierarchy_level: number; - is_custom: boolean; - name: string; - }; + account_id: string | null + hierarchy_level: number + is_custom: boolean + name: string + } Insert: { - account_id?: string | null; - hierarchy_level: number; - is_custom?: boolean; - name: string; - }; + account_id?: string | null + hierarchy_level: number + is_custom?: boolean + name: string + } Update: { - account_id?: string | null; - hierarchy_level?: number; - is_custom?: boolean; - name?: string; - }; + account_id?: string | null + hierarchy_level?: number + is_custom?: boolean + name?: string + } Relationships: [ { - foreignKeyName: 'roles_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'accounts'; - referencedColumns: ['id']; + foreignKeyName: "roles_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "accounts" + referencedColumns: ["id"] }, { - foreignKeyName: 'roles_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_account_workspace'; - referencedColumns: ['id']; + foreignKeyName: "roles_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_account_workspace" + referencedColumns: ["id"] }, { - foreignKeyName: 'roles_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_accounts'; - referencedColumns: ['id']; + foreignKeyName: "roles_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_accounts" + referencedColumns: ["id"] }, - ]; - }; + ] + } subscriptions: { Row: { - account_id: string; - active: boolean; - billing_customer_id: number; - billing_provider: Database['public']['Enums']['billing_provider']; - cancel_at_period_end: boolean; - created_at: string; - currency: string; - id: string; - interval: string; - interval_count: number; - period_ends_at: string | null; - period_starts_at: string | null; - price_amount: number | null; - product_id: string; - status: Database['public']['Enums']['subscription_status']; - trial_ends_at: string | null; - trial_starts_at: string | null; - updated_at: string; - variant_id: string; - }; + account_id: string + active: boolean + billing_customer_id: number + billing_provider: Database["public"]["Enums"]["billing_provider"] + cancel_at_period_end: boolean + created_at: string + currency: string + id: string + interval: string + interval_count: number + period_ends_at: string | null + period_starts_at: string | null + price_amount: number | null + product_id: string + status: Database["public"]["Enums"]["subscription_status"] + trial_ends_at: string | null + trial_starts_at: string | null + updated_at: string + variant_id: string + } Insert: { - account_id: string; - active: boolean; - billing_customer_id: number; - billing_provider: Database['public']['Enums']['billing_provider']; - cancel_at_period_end: boolean; - created_at?: string; - currency: string; - id: string; - interval: string; - interval_count: number; - period_ends_at?: string | null; - period_starts_at?: string | null; - price_amount?: number | null; - product_id: string; - status: Database['public']['Enums']['subscription_status']; - trial_ends_at?: string | null; - trial_starts_at?: string | null; - updated_at?: string; - variant_id: string; - }; + account_id: string + active: boolean + billing_customer_id: number + billing_provider: Database["public"]["Enums"]["billing_provider"] + cancel_at_period_end: boolean + created_at?: string + currency: string + id: string + interval: string + interval_count: number + period_ends_at?: string | null + period_starts_at?: string | null + price_amount?: number | null + product_id: string + status: Database["public"]["Enums"]["subscription_status"] + trial_ends_at?: string | null + trial_starts_at?: string | null + updated_at?: string + variant_id: string + } Update: { - account_id?: string; - active?: boolean; - billing_customer_id?: number; - billing_provider?: Database['public']['Enums']['billing_provider']; - cancel_at_period_end?: boolean; - created_at?: string; - currency?: string; - id?: string; - interval?: string; - interval_count?: number; - period_ends_at?: string | null; - period_starts_at?: string | null; - price_amount?: number | null; - product_id?: string; - status?: Database['public']['Enums']['subscription_status']; - trial_ends_at?: string | null; - trial_starts_at?: string | null; - updated_at?: string; - variant_id?: string; - }; + account_id?: string + active?: boolean + billing_customer_id?: number + billing_provider?: Database["public"]["Enums"]["billing_provider"] + cancel_at_period_end?: boolean + created_at?: string + currency?: string + id?: string + interval?: string + interval_count?: number + period_ends_at?: string | null + period_starts_at?: string | null + price_amount?: number | null + product_id?: string + status?: Database["public"]["Enums"]["subscription_status"] + trial_ends_at?: string | null + trial_starts_at?: string | null + updated_at?: string + variant_id?: string + } Relationships: [ { - foreignKeyName: 'subscriptions_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'accounts'; - referencedColumns: ['id']; + foreignKeyName: "subscriptions_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "accounts" + referencedColumns: ["id"] }, { - foreignKeyName: 'subscriptions_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_account_workspace'; - referencedColumns: ['id']; + foreignKeyName: "subscriptions_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_account_workspace" + referencedColumns: ["id"] }, { - foreignKeyName: 'subscriptions_account_id_fkey'; - columns: ['account_id']; - isOneToOne: false; - referencedRelation: 'user_accounts'; - referencedColumns: ['id']; + foreignKeyName: "subscriptions_account_id_fkey" + columns: ["account_id"] + isOneToOne: false + referencedRelation: "user_accounts" + referencedColumns: ["id"] }, { - foreignKeyName: 'subscriptions_billing_customer_id_fkey'; - columns: ['billing_customer_id']; - isOneToOne: false; - referencedRelation: 'billing_customers'; - referencedColumns: ['id']; + foreignKeyName: "subscriptions_billing_customer_id_fkey" + columns: ["billing_customer_id"] + isOneToOne: false + referencedRelation: "billing_customers" + referencedColumns: ["id"] }, - ]; - }; - }; + ] + } + } Views: { user_account_workspace: { Row: { - id: string | null; - name: string | null; - picture_url: string | null; + id: string | null + name: string | null + picture_url: string | null subscription_status: - | Database['public']['Enums']['subscription_status'] - | null; - }; - Relationships: []; - }; + | Database["public"]["Enums"]["subscription_status"] + | null + } + Relationships: [] + } user_accounts: { Row: { - id: string | null; - name: string | null; - picture_url: string | null; - role: string | null; - slug: string | null; - }; + id: string | null + name: string | null + picture_url: string | null + role: string | null + slug: string | null + } Relationships: [ { - foreignKeyName: 'accounts_memberships_account_role_fkey'; - columns: ['role']; - isOneToOne: false; - referencedRelation: 'roles'; - referencedColumns: ['name']; + foreignKeyName: "accounts_memberships_account_role_fkey" + columns: ["role"] + isOneToOne: false + referencedRelation: "roles" + referencedColumns: ["name"] }, - ]; - }; - }; + ] + } + } Functions: { accept_invitation: { Args: { - token: string; - user_id: string; - }; - Returns: undefined; - }; + token: string + user_id: string + } + Returns: undefined + } add_invitations_to_account: { Args: { - account_slug: string; - invitations: unknown[]; - }; - Returns: Database['public']['Tables']['invitations']['Row'][]; - }; + account_slug: string + invitations: unknown[] + } + Returns: Database["public"]["Tables"]["invitations"]["Row"][] + } add_subscription: { Args: { - account_id: string; - subscription_id: string; - active: boolean; - status: Database['public']['Enums']['subscription_status']; - billing_provider: Database['public']['Enums']['billing_provider']; - product_id: string; - variant_id: string; - price_amount: number; - cancel_at_period_end: boolean; - currency: string; - interval: string; - interval_count: number; - period_starts_at: string; - period_ends_at: string; - trial_starts_at: string; - trial_ends_at: string; - customer_id: string; - }; + account_id: string + subscription_id: string + active: boolean + status: Database["public"]["Enums"]["subscription_status"] + billing_provider: Database["public"]["Enums"]["billing_provider"] + product_id: string + variant_id: string + price_amount: number + cancel_at_period_end: boolean + currency: string + interval: string + interval_count: number + period_starts_at: string + period_ends_at: string + trial_starts_at: string + trial_ends_at: string + customer_id: string + } Returns: { - account_id: string; - active: boolean; - billing_customer_id: number; - billing_provider: Database['public']['Enums']['billing_provider']; - cancel_at_period_end: boolean; - created_at: string; - currency: string; - id: string; - interval: string; - interval_count: number; - period_ends_at: string | null; - period_starts_at: string | null; - price_amount: number | null; - product_id: string; - status: Database['public']['Enums']['subscription_status']; - trial_ends_at: string | null; - trial_starts_at: string | null; - updated_at: string; - variant_id: string; - }; - }; + account_id: string + active: boolean + billing_customer_id: number + billing_provider: Database["public"]["Enums"]["billing_provider"] + cancel_at_period_end: boolean + created_at: string + currency: string + id: string + interval: string + interval_count: number + period_ends_at: string | null + period_starts_at: string | null + price_amount: number | null + product_id: string + status: Database["public"]["Enums"]["subscription_status"] + trial_ends_at: string | null + trial_starts_at: string | null + updated_at: string + variant_id: string + } + } create_account: { Args: { - account_name: string; - }; + account_name: string + } Returns: { - created_at: string | null; - created_by: string | null; - email: string | null; - id: string; - is_personal_account: boolean; - name: string; - picture_url: string | null; - primary_owner_user_id: string; - slug: string | null; - updated_at: string | null; - updated_by: string | null; - }; - }; + created_at: string | null + created_by: string | null + email: string | null + id: string + is_personal_account: boolean + name: string + picture_url: string | null + primary_owner_user_id: string + slug: string | null + updated_at: string | null + updated_by: string | null + } + } create_invitation: { Args: { - account_id: string; - email: string; - role: string; - }; + account_id: string + email: string + role: string + } Returns: { - account_id: string; - created_at: string; - email: string; - expires_at: string; - id: number; - invite_token: string; - invited_by: string; - role: string; - updated_at: string; - }; - }; + account_id: string + created_at: string + email: string + expires_at: string + id: number + invite_token: string + invited_by: string + role: string + updated_at: string + } + } get_account_invitations: { Args: { - account_slug: string; - }; + account_slug: string + } Returns: { - id: number; - email: string; - account_id: string; - invited_by: string; - role: string; - created_at: string; - updated_at: string; - expires_at: string; - inviter_name: string; - inviter_email: string; - }[]; - }; + id: number + email: string + account_id: string + invited_by: string + role: string + created_at: string + updated_at: string + expires_at: string + inviter_name: string + inviter_email: string + }[] + } get_account_members: { Args: { - account_slug: string; - }; + account_slug: string + } Returns: { - id: string; - user_id: string; - account_id: string; - role: string; - role_hierarchy_level: number; - primary_owner_user_id: string; - name: string; - email: string; - picture_url: string; - created_at: string; - updated_at: string; - }[]; - }; + id: string + user_id: string + account_id: string + role: string + role_hierarchy_level: number + primary_owner_user_id: string + name: string + email: string + picture_url: string + created_at: string + updated_at: string + }[] + } get_config: { - Args: Record; - Returns: Json; - }; + Args: Record + Returns: Json + } get_user_accounts: { - Args: Record; + Args: Record Returns: { - created_at: string | null; - created_by: string | null; - email: string | null; - id: string; - is_personal_account: boolean; - name: string; - picture_url: string | null; - primary_owner_user_id: string; - slug: string | null; - updated_at: string | null; - updated_by: string | null; - }[]; - }; + created_at: string | null + created_by: string | null + email: string | null + id: string + is_personal_account: boolean + name: string + picture_url: string | null + primary_owner_user_id: string + slug: string | null + updated_at: string | null + updated_by: string | null + }[] + } has_more_elevated_role: { Args: { - target_user_id: string; - target_account_id: string; - role_name: string; - }; - Returns: boolean; - }; + target_user_id: string + target_account_id: string + role_name: string + } + Returns: boolean + } has_permission: { Args: { - user_id: string; - account_id: string; - permission_name: Database['public']['Enums']['app_permissions']; - }; - Returns: boolean; - }; + user_id: string + account_id: string + permission_name: Database["public"]["Enums"]["app_permissions"] + } + Returns: boolean + } has_role_on_account: { Args: { - account_id: string; - account_role?: string; - }; - Returns: boolean; - }; + account_id: string + account_role?: string + } + Returns: boolean + } is_account_owner: { Args: { - account_id: string; - }; - Returns: boolean; - }; + account_id: string + } + Returns: boolean + } is_set: { Args: { - field_name: string; - }; - Returns: boolean; - }; + field_name: string + } + Returns: boolean + } is_team_member: { Args: { - account_id: string; - user_id: string; - }; - Returns: boolean; - }; + account_id: string + user_id: string + } + Returns: boolean + } organization_account_workspace: { Args: { - account_slug: string; - }; + account_slug: string + } Returns: { - id: string; - name: string; - picture_url: string; - slug: string; - role: string; - role_hierarchy_level: number; - primary_owner_user_id: string; - subscription_status: Database['public']['Enums']['subscription_status']; - permissions: Database['public']['Enums']['app_permissions'][]; - }[]; - }; + id: string + name: string + picture_url: string + slug: string + role: string + role_hierarchy_level: number + primary_owner_user_id: string + subscription_status: Database["public"]["Enums"]["subscription_status"] + permissions: Database["public"]["Enums"]["app_permissions"][] + }[] + } + transfer_team_account_ownership: { + Args: { + target_account_id: string + new_owner_id: string + } + Returns: undefined + } unaccent: { Args: { - '': string; - }; - Returns: string; - }; + "": string + } + Returns: string + } unaccent_init: { Args: { - '': unknown; - }; - Returns: unknown; - }; - }; + "": unknown + } + Returns: unknown + } + } Enums: { app_permissions: - | 'roles.manage' - | 'billing.manage' - | 'settings.manage' - | 'members.manage' - | 'invites.manage'; - billing_provider: 'stripe' | 'lemon-squeezy' | 'paddle'; + | "roles.manage" + | "billing.manage" + | "settings.manage" + | "members.manage" + | "invites.manage" + billing_provider: "stripe" | "lemon-squeezy" | "paddle" subscription_status: - | 'active' - | 'trialing' - | 'past_due' - | 'canceled' - | 'unpaid' - | 'incomplete' - | 'incomplete_expired' - | 'paused'; - }; + | "active" + | "trialing" + | "past_due" + | "canceled" + | "unpaid" + | "incomplete" + | "incomplete_expired" + | "paused" + } CompositeTypes: { - [_ in never]: never; - }; - }; + [_ 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; - }; + 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: { - 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; - }; + 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 + } 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: []; - }; + 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; - }; + executed_at: string | null + hash: string + id: number + name: string + } Insert: { - executed_at?: string | null; - hash: string; - id: number; - name: string; - }; + executed_at?: string | null + hash: string + id: number + name: string + } Update: { - executed_at?: string | null; - hash?: string; - id?: number; - name?: string; - }; - Relationships: []; - }; + 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; - }; + 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; - }; + 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; - }; + 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']; + foreignKeyName: "objects_bucketId_fkey" + columns: ["bucket_id"] + isOneToOne: false + referencedRelation: "buckets" + referencedColumns: ["id"] }, - ]; - }; - }; + ] + } + } Views: { - [_ in never]: never; - }; + [_ in never]: never + } Functions: { can_insert_object: { Args: { - bucketid: string; - name: string; - owner: string; - metadata: Json; - }; - Returns: undefined; - }; + bucketid: string + name: string + owner: string + metadata: Json + } + Returns: undefined + } extension: { Args: { - name: string; - }; - Returns: string; - }; + name: string + } + Returns: string + } filename: { Args: { - name: string; - }; - Returns: string; - }; + name: string + } + Returns: string + } foldername: { Args: { - name: string; - }; - Returns: string[]; - }; + name: string + } + Returns: string[] + } get_size_by_bucket: { - Args: Record; + Args: Record Returns: { - size: number; - bucket_id: string; - }[]; - }; + size: number + bucket_id: string + }[] + } search: { Args: { - prefix: string; - bucketname: string; - limits?: number; - levels?: number; - offsets?: number; - search?: string; - sortcolumn?: string; - sortorder?: string; - }; + 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; - }[]; - }; - }; + name: string + id: string + updated_at: string + created_at: string + last_accessed_at: string + metadata: Json + }[] + } + } Enums: { - [_ in never]: never; - }; + [_ in never]: never + } CompositeTypes: { - [_ in never]: never; - }; - }; -}; + [_ in never]: never + } + } +} -type PublicSchema = Database[Extract]; +type PublicSchema = Database[Extract] export type Tables< PublicTableNameOrOptions extends - | keyof (PublicSchema['Tables'] & PublicSchema['Views']) + | keyof (PublicSchema["Tables"] & PublicSchema["Views"]) | { schema: keyof Database }, TableName extends PublicTableNameOrOptions extends { schema: keyof Database } - ? keyof (Database[PublicTableNameOrOptions['schema']]['Tables'] & - Database[PublicTableNameOrOptions['schema']]['Views']) + ? keyof (Database[PublicTableNameOrOptions["schema"]]["Tables"] & + Database[PublicTableNameOrOptions["schema"]]["Views"]) : never = never, > = PublicTableNameOrOptions extends { schema: keyof Database } - ? (Database[PublicTableNameOrOptions['schema']]['Tables'] & - Database[PublicTableNameOrOptions['schema']]['Views'])[TableName] extends { - Row: infer R; + ? (Database[PublicTableNameOrOptions["schema"]]["Tables"] & + Database[PublicTableNameOrOptions["schema"]]["Views"])[TableName] extends { + Row: infer R } ? R : never - : PublicTableNameOrOptions extends keyof (PublicSchema['Tables'] & - PublicSchema['Views']) - ? (PublicSchema['Tables'] & - PublicSchema['Views'])[PublicTableNameOrOptions] extends { - Row: infer R; + : PublicTableNameOrOptions extends keyof (PublicSchema["Tables"] & + PublicSchema["Views"]) + ? (PublicSchema["Tables"] & + PublicSchema["Views"])[PublicTableNameOrOptions] extends { + Row: infer R } ? R : never - : never; + : never export type TablesInsert< PublicTableNameOrOptions extends - | keyof PublicSchema['Tables'] + | keyof PublicSchema["Tables"] | { schema: keyof Database }, TableName extends PublicTableNameOrOptions extends { schema: keyof Database } - ? keyof Database[PublicTableNameOrOptions['schema']]['Tables'] + ? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"] : never = never, > = PublicTableNameOrOptions extends { schema: keyof Database } - ? Database[PublicTableNameOrOptions['schema']]['Tables'][TableName] extends { - Insert: infer I; + ? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Insert: infer I } ? I : never - : PublicTableNameOrOptions extends keyof PublicSchema['Tables'] - ? PublicSchema['Tables'][PublicTableNameOrOptions] extends { - Insert: infer I; + : PublicTableNameOrOptions extends keyof PublicSchema["Tables"] + ? PublicSchema["Tables"][PublicTableNameOrOptions] extends { + Insert: infer I } ? I : never - : never; + : never export type TablesUpdate< PublicTableNameOrOptions extends - | keyof PublicSchema['Tables'] + | keyof PublicSchema["Tables"] | { schema: keyof Database }, TableName extends PublicTableNameOrOptions extends { schema: keyof Database } - ? keyof Database[PublicTableNameOrOptions['schema']]['Tables'] + ? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"] : never = never, > = PublicTableNameOrOptions extends { schema: keyof Database } - ? Database[PublicTableNameOrOptions['schema']]['Tables'][TableName] extends { - Update: infer U; + ? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Update: infer U } ? U : never - : PublicTableNameOrOptions extends keyof PublicSchema['Tables'] - ? PublicSchema['Tables'][PublicTableNameOrOptions] extends { - Update: infer U; + : PublicTableNameOrOptions extends keyof PublicSchema["Tables"] + ? PublicSchema["Tables"][PublicTableNameOrOptions] extends { + Update: infer U } ? U : never - : never; + : never export type Enums< PublicEnumNameOrOptions extends - | keyof PublicSchema['Enums'] + | keyof PublicSchema["Enums"] | { schema: keyof Database }, EnumName extends PublicEnumNameOrOptions extends { schema: keyof Database } - ? keyof Database[PublicEnumNameOrOptions['schema']]['Enums'] + ? keyof Database[PublicEnumNameOrOptions["schema"]]["Enums"] : never = never, > = PublicEnumNameOrOptions extends { schema: keyof Database } - ? Database[PublicEnumNameOrOptions['schema']]['Enums'][EnumName] - : PublicEnumNameOrOptions extends keyof PublicSchema['Enums'] - ? PublicSchema['Enums'][PublicEnumNameOrOptions] - : never; + ? Database[PublicEnumNameOrOptions["schema"]]["Enums"][EnumName] + : PublicEnumNameOrOptions extends keyof PublicSchema["Enums"] + ? PublicSchema["Enums"][PublicEnumNameOrOptions] + : never + diff --git a/supabase/migrations/20221215192558_schema.sql b/supabase/migrations/20221215192558_schema.sql index 77e38710d..cd1e4b2c5 100644 --- a/supabase/migrations/20221215192558_schema.sql +++ b/supabase/migrations/20221215192558_schema.sql @@ -314,6 +314,35 @@ with check (auth.uid () = primary_owner_user_id); -- Functions +-- Function to transfer team account ownership to another user +create or replace function public.transfer_team_account_ownership (target_account_id uuid, new_owner_id uuid) returns void as $$ +begin + if current_user not in('service_role') then + raise exception 'You do not have permission to transfer account ownership'; + end if; + + -- update the primary owner of the account + update public.accounts + set primary_owner_user_id = new_owner_id + where id = target_account_id and is_personal_account = false; + + -- update membership assigning it the hierarchy role + update public.accounts_memberships + set account_role = ( + select + name + from + public.roles + where + hierarchy_level = 1 + ) + where target_account_id = account_id and user_id = new_owner_id; + +end; +$$ language plpgsql; + +grant execute on function public.transfer_team_account_ownership (uuid, uuid) to service_role; + create function public.is_account_owner (account_id uuid) returns boolean as $$ select exists( @@ -326,6 +355,8 @@ create function public.is_account_owner (account_id uuid) returns boolean as $$ and primary_owner_user_id = auth.uid()); $$ language sql; +grant execute on function public.is_account_owner (uuid) to authenticated, service_role; + create or replace function kit.protect_account_fields () returns trigger as $$ begin