mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-04 21:04:37 +08:00
Project owner packs
This commit is contained in:
parent
82e0cf505a
commit
448c45fca4
@ -5,11 +5,20 @@ import { createCrudHandlers } from "@/route-handlers/crud-handler";
|
||||
import { KnownErrors } from "@stackframe/stack-shared";
|
||||
import { internalProjectsCrud } from "@stackframe/stack-shared/dist/interface/crud/projects";
|
||||
import { projectIdSchema, yupObject } from "@stackframe/stack-shared/dist/schema-fields";
|
||||
import { StackAssertionError, throwErr } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { StackAssertionError, captureError, throwErr } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { createLazyProxy } from "@stackframe/stack-shared/dist/utils/proxies";
|
||||
import { typedToUppercase } from "@stackframe/stack-shared/dist/utils/strings";
|
||||
import { generateUuid } from "@stackframe/stack-shared/dist/utils/uuids";
|
||||
|
||||
// if one of these users creates a project, the others will be added as owners
|
||||
const ownerPacks = [
|
||||
new Set([
|
||||
"c2c03bd1-5cbe-4493-8e3f-17d1e2d7ca43",
|
||||
"60b859bf-e148-4eff-9985-fe6e31c58a2a",
|
||||
"1343e3e7-dd7a-44a1-8752-701c0881da72",
|
||||
]),
|
||||
];
|
||||
|
||||
export const internalProjectsCrudHandlers = createLazyProxy(() => createCrudHandlers(internalProjectsCrud, {
|
||||
paramsSchema: yupObject({
|
||||
projectId: projectIdSchema.required(),
|
||||
@ -24,6 +33,8 @@ export const internalProjectsCrudHandlers = createLazyProxy(() => createCrudHand
|
||||
},
|
||||
onCreate: async ({ auth, data }) => {
|
||||
const user = auth.user ?? throwErr('auth.user is required');
|
||||
const ownerPack = ownerPacks.find(p => p.has(user.id));
|
||||
const userIds = ownerPack ? [...ownerPack] : [user.id];
|
||||
|
||||
const result = await prismaClient.$transaction(async (tx) => {
|
||||
const project = await tx.project.create({
|
||||
@ -126,34 +137,45 @@ export const internalProjectsCrudHandlers = createLazyProxy(() => createCrudHand
|
||||
},
|
||||
});
|
||||
|
||||
const projectUserTx = await tx.projectUser.findUniqueOrThrow({
|
||||
where: {
|
||||
projectId_projectUserId: {
|
||||
projectId: "internal",
|
||||
projectUserId: user.id,
|
||||
// Update owner metadata
|
||||
for (const userId of userIds) {
|
||||
const projectUserTx = await tx.projectUser.findUnique({
|
||||
where: {
|
||||
projectId_projectUserId: {
|
||||
projectId: "internal",
|
||||
projectUserId: userId,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
if (!projectUserTx) {
|
||||
if (userId === user.id) {
|
||||
throw new StackAssertionError(`User with id '${userId}' not found after creation`, { user });
|
||||
} else {
|
||||
captureError("project-creation-owner-packs", new StackAssertionError(`User ${userId} in owner pack not found. The user that created this project is in an owner pack, but no user with that ID was found. Did they delete their account? Continuing silently, but you should probably update the owner pack.`, { userId, creator: user }));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const serverMetadataTx: any = projectUserTx.serverMetadata ?? {};
|
||||
const serverMetadataTx: any = projectUserTx.serverMetadata ?? {};
|
||||
|
||||
await tx.projectUser.update({
|
||||
where: {
|
||||
projectId_projectUserId: {
|
||||
projectId: "internal",
|
||||
projectUserId: projectUserTx.projectUserId,
|
||||
await tx.projectUser.update({
|
||||
where: {
|
||||
projectId_projectUserId: {
|
||||
projectId: "internal",
|
||||
projectUserId: projectUserTx.projectUserId,
|
||||
},
|
||||
},
|
||||
},
|
||||
data: {
|
||||
serverMetadata: {
|
||||
...serverMetadataTx ?? {},
|
||||
managedProjectIds: [
|
||||
...serverMetadataTx?.managedProjectIds ?? [],
|
||||
project.id,
|
||||
],
|
||||
data: {
|
||||
serverMetadata: {
|
||||
...serverMetadataTx ?? {},
|
||||
managedProjectIds: [
|
||||
...serverMetadataTx?.managedProjectIds ?? [],
|
||||
project.id,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const result = await tx.project.findUnique({
|
||||
where: { id: project.id },
|
||||
|
||||
@ -192,11 +192,13 @@ export const getCommonUserColumns = <T extends ExtendedServerUser>() => [
|
||||
accessorKey: "id",
|
||||
header: ({ column }) => <DataTableColumnHeader column={column} columnTitle="ID" />,
|
||||
cell: ({ row }) => <TextCell size={60}>{row.original.id}</TextCell>,
|
||||
enableGlobalFilter: true,
|
||||
},
|
||||
{
|
||||
accessorKey: "displayName",
|
||||
header: ({ column }) => <DataTableColumnHeader column={column} columnTitle="Display Name" />,
|
||||
cell: ({ row }) => <TextCell size={120}>{row.original.displayName}</TextCell>,
|
||||
cell: ({ row }) => <TextCell size={120}><span className={row.original.displayName === null ? 'text-slate-400' : ''}>{row.original.displayName ?? '–'}</span></TextCell>,
|
||||
enableGlobalFilter: true,
|
||||
},
|
||||
{
|
||||
accessorKey: "primaryEmail",
|
||||
@ -206,12 +208,14 @@ export const getCommonUserColumns = <T extends ExtendedServerUser>() => [
|
||||
icon={row.original.emailVerified === "unverified" && <SimpleTooltip tooltip='Email not verified' type='warning'/>}>
|
||||
{row.original.primaryEmail}
|
||||
</TextCell>,
|
||||
enableGlobalFilter: true,
|
||||
},
|
||||
{
|
||||
accessorKey: "emailVerified",
|
||||
header: ({ column }) => <DataTableColumnHeader column={column} columnTitle="Email Verified" />,
|
||||
cell: ({ row }) => <TextCell>{row.original.emailVerified === 'verified' ? '✓' : '✗'}</TextCell>,
|
||||
filterFn: standardFilterFn
|
||||
filterFn: standardFilterFn,
|
||||
enableGlobalFilter: false,
|
||||
},
|
||||
] satisfies ColumnDef<T>[];
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ import {
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
GlobalFiltering,
|
||||
SortingState,
|
||||
Table as TableType,
|
||||
VisibilityState,
|
||||
@ -26,6 +27,7 @@ import {
|
||||
import React from "react";
|
||||
import { DataTablePagination } from "./pagination";
|
||||
import { DataTableToolbar } from "./toolbar";
|
||||
import { ColumnFilter } from "@tanstack/react-table";
|
||||
|
||||
interface DataTableProps<TData, TValue> {
|
||||
columns: ColumnDef<TData, TValue>[],
|
||||
@ -45,7 +47,7 @@ export function DataTable<TData, TValue>({
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
|
||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||
|
||||
const table = useReactTable({
|
||||
const table: TableType<TData> = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
state: {
|
||||
@ -59,6 +61,7 @@ export function DataTable<TData, TValue>({
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
getColumnCanGlobalFilter: (c) => c.columnDef.enableGlobalFilter ?? GlobalFiltering.getDefaultOptions!(table).getColumnCanGlobalFilter!(c),
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user