feat: add admin-only endpoint for project permissions with detailed environment data

Introduced a new API endpoint `allForPermissions` to retrieve projects along with their environments and services specifically for admin users. This enhancement allows for a more comprehensive permissions UI by including detailed information about each environment and its associated applications, improving the overall user experience in managing permissions.
This commit is contained in:
Mauricio Siu
2026-03-01 14:02:00 -06:00
parent 149293f4d3
commit a360a259f5
2 changed files with 114 additions and 4 deletions

View File

@@ -28,8 +28,12 @@ import {
import { Switch } from "@/components/ui/switch";
import { api, type RouterOutputs } from "@/utils/api";
type Project = RouterOutputs["project"]["all"][number];
type Environment = Project["environments"][number];
/** Shape returned by project.allForPermissions (admin only). Used for the permissions UI. */
type ProjectForPermissions = RouterOutputs["project"]["allForPermissions"][number];
type EnvironmentForPermissions = ProjectForPermissions["environments"][number];
type Project = ProjectForPermissions;
type Environment = EnvironmentForPermissions;
export type Services = {
appName: string;
@@ -173,7 +177,9 @@ interface Props {
export const AddUserPermissions = ({ userId }: Props) => {
const [isOpen, setIsOpen] = useState(false);
const { data: projects } = api.project.all.useQuery();
const { data: projects } = api.project.allForPermissions.useQuery(undefined, {
enabled: isOpen,
});
const { data, refetch } = api.user.one.useQuery(
{

View File

@@ -37,7 +37,11 @@ import { TRPCError } from "@trpc/server";
import { and, desc, eq, ilike, or, sql } from "drizzle-orm";
import type { AnyPgColumn } from "drizzle-orm/pg-core";
import { z } from "zod";
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
import {
adminProcedure,
createTRPCRouter,
protectedProcedure,
} from "@/server/api/trpc";
import {
apiCreateProject,
apiFindOneProject,
@@ -332,6 +336,106 @@ export const projectRouter = createTRPCRouter({
});
}),
/** All projects with full environments and services for the admin permissions UI. Admin only. */
allForPermissions: adminProcedure.query(async ({ ctx }) => {
return await db.query.projects.findMany({
where: eq(projects.organizationId, ctx.session.activeOrganizationId),
orderBy: desc(projects.createdAt),
columns: {
projectId: true,
name: true,
},
with: {
environments: {
columns: {
environmentId: true,
name: true,
isDefault: true,
},
with: {
applications: {
columns: {
applicationId: true,
appName: true,
name: true,
createdAt: true,
applicationStatus: true,
description: true,
serverId: true,
},
},
mariadb: {
columns: {
mariadbId: true,
appName: true,
name: true,
createdAt: true,
applicationStatus: true,
description: true,
serverId: true,
},
},
postgres: {
columns: {
postgresId: true,
appName: true,
name: true,
createdAt: true,
applicationStatus: true,
description: true,
serverId: true,
},
},
mysql: {
columns: {
mysqlId: true,
appName: true,
name: true,
createdAt: true,
applicationStatus: true,
description: true,
serverId: true,
},
},
mongo: {
columns: {
mongoId: true,
appName: true,
name: true,
createdAt: true,
applicationStatus: true,
description: true,
serverId: true,
},
},
redis: {
columns: {
redisId: true,
appName: true,
name: true,
createdAt: true,
applicationStatus: true,
description: true,
serverId: true,
},
},
compose: {
columns: {
composeId: true,
appName: true,
name: true,
createdAt: true,
composeStatus: true,
description: true,
serverId: true,
},
},
},
},
},
});
}),
search: protectedProcedure
.input(
z.object({