diff --git a/apps/dokploy/components/dashboard/project/duplicate-project.tsx b/apps/dokploy/components/dashboard/project/duplicate-project.tsx index f84cf35dd..e754b1d8b 100644 --- a/apps/dokploy/components/dashboard/project/duplicate-project.tsx +++ b/apps/dokploy/components/dashboard/project/duplicate-project.tsx @@ -25,7 +25,6 @@ import { import { api } from "@/utils/api"; export type Services = { - appName: string; serverId?: string | null; name: string; type: diff --git a/apps/dokploy/components/dashboard/projects/show.tsx b/apps/dokploy/components/dashboard/projects/show.tsx index c3d4d498b..f25fb6d47 100644 --- a/apps/dokploy/components/dashboard/projects/show.tsx +++ b/apps/dokploy/components/dashboard/projects/show.tsx @@ -2,7 +2,6 @@ import { AlertTriangle, ArrowUpDown, BookIcon, - ExternalLinkIcon, FolderInput, Loader2, MoreHorizontalIcon, @@ -16,7 +15,6 @@ import { toast } from "sonner"; import { BreadcrumbSidebar } from "@/components/shared/breadcrumb-sidebar"; import { DateTooltip } from "@/components/shared/date-tooltip"; import { FocusShortcutInput } from "@/components/shared/focus-shortcut-input"; -import { StatusTooltip } from "@/components/shared/status-tooltip"; import { AlertDialog, AlertDialogAction, @@ -40,10 +38,8 @@ import { import { DropdownMenu, DropdownMenuContent, - DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, - DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { @@ -280,14 +276,6 @@ export const ShowProjects = () => { ) .reduce((acc, curr) => acc + curr, 0); - const haveServicesWithDomains = project?.environments - .map( - (env) => - env.applications.length > 0 || - env.compose.length > 0, - ) - .some(Boolean); - // Find default environment from accessible environments, or fall back to first accessible environment const accessibleEnvironment = project?.environments.find((env) => env.isDefault) || @@ -313,122 +301,6 @@ export const ShowProjects = () => { }} > - {haveServicesWithDomains ? ( - - - - - e.stopPropagation()} - > - {project.environments.some( - (env) => env.applications.length > 0, - ) && ( - - - Applications - - {project.environments.map((env) => - env.applications.map((app) => ( -
- - - - {app.name} - - - - {app.domains.map((domain) => ( - - - - {domain.host} - - - - - ))} - -
- )), - )} -
- )} - {project.environments.some( - (env) => env.compose.length > 0, - ) && ( - - - Compose - - {project.environments.map((env) => - env.compose.map((comp) => ( -
- - - - {comp.name} - - - - {comp.domains.map((domain) => ( - - - - {domain.host} - - - - - ))} - -
- )), - )} -
- )} -
-
- ) : null} diff --git a/apps/dokploy/components/dashboard/settings/servers/setup-monitoring.tsx b/apps/dokploy/components/dashboard/settings/servers/setup-monitoring.tsx index 6468ce25d..701593436 100644 --- a/apps/dokploy/components/dashboard/settings/servers/setup-monitoring.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/setup-monitoring.tsx @@ -100,7 +100,7 @@ export const SetupMonitoring = ({ serverId }: Props) => { const url = useUrl(); - const { data: projects } = api.project.all.useQuery(); + const { data: projects } = api.project.allForPermissions.useQuery(); const extractServicesFromProjects = () => { if (!projects) return []; diff --git a/apps/dokploy/components/dashboard/settings/users/add-permissions.tsx b/apps/dokploy/components/dashboard/settings/users/add-permissions.tsx index d3f8af31c..d0a2a26fa 100644 --- a/apps/dokploy/components/dashboard/settings/users/add-permissions.tsx +++ b/apps/dokploy/components/dashboard/settings/users/add-permissions.tsx @@ -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 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( { diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx index af901311e..89d77af24 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx @@ -100,7 +100,6 @@ import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; export type Services = { - appName: string; serverId?: string | null; serverName?: string | null; name: string; @@ -146,7 +145,6 @@ export const extractServicesFromEnvironment = ( } } return { - appName: item.appName, name: item.name, type: "application", id: item.applicationId, @@ -161,7 +159,6 @@ export const extractServicesFromEnvironment = ( const mariadb: Services[] = environment.mariadb?.map((item) => ({ - appName: item.appName, name: item.name, type: "mariadb", id: item.mariadbId, @@ -174,7 +171,6 @@ export const extractServicesFromEnvironment = ( const postgres: Services[] = environment.postgres?.map((item) => ({ - appName: item.appName, name: item.name, type: "postgres", id: item.postgresId, @@ -187,7 +183,6 @@ export const extractServicesFromEnvironment = ( const mongo: Services[] = environment.mongo?.map((item) => ({ - appName: item.appName, name: item.name, type: "mongo", id: item.mongoId, @@ -200,7 +195,6 @@ export const extractServicesFromEnvironment = ( const redis: Services[] = environment.redis?.map((item) => ({ - appName: item.appName, name: item.name, type: "redis", id: item.redisId, @@ -213,7 +207,6 @@ export const extractServicesFromEnvironment = ( const mysql: Services[] = environment.mysql?.map((item) => ({ - appName: item.appName, name: item.name, type: "mysql", id: item.mysqlId, @@ -242,7 +235,6 @@ export const extractServicesFromEnvironment = ( } } return { - appName: item.appName, name: item.name, type: "compose", id: item.composeId, diff --git a/apps/dokploy/server/api/routers/project.ts b/apps/dokploy/server/api/routers/project.ts index ee353594c..e270ee4b4 100644 --- a/apps/dokploy/server/api/routers/project.ts +++ b/apps/dokploy/server/api/routers/project.ts @@ -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, @@ -219,31 +223,69 @@ export const projectRouter = createTRPCRouter({ applications.applicationId, accessedServices, ), - with: { domains: true }, + columns: { + applicationId: true, + name: true, + applicationStatus: true, + }, }, mariadb: { where: buildServiceFilter(mariadb.mariadbId, accessedServices), + columns: { + mariadbId: true, + name: true, + applicationStatus: true, + }, }, mongo: { where: buildServiceFilter(mongo.mongoId, accessedServices), + columns: { + mongoId: true, + name: true, + applicationStatus: true, + }, }, mysql: { where: buildServiceFilter(mysql.mysqlId, accessedServices), + columns: { + mysqlId: true, + name: true, + applicationStatus: true, + }, }, postgres: { where: buildServiceFilter( postgres.postgresId, accessedServices, ), + columns: { + postgresId: true, + name: true, + applicationStatus: true, + }, }, redis: { where: buildServiceFilter(redis.redisId, accessedServices), + columns: { + redisId: true, + name: true, + applicationStatus: true, + }, }, compose: { where: buildServiceFilter(compose.composeId, accessedServices), - with: { domains: true }, + columns: { + composeId: true, + name: true, + composeStatus: true, + }, }, }, + columns: { + environmentId: true, + isDefault: true, + name: true, + }, }, }, orderBy: desc(projects.createdAt), @@ -255,21 +297,50 @@ export const projectRouter = createTRPCRouter({ environments: { with: { applications: { - with: { - domains: true, + columns: { + applicationId: true, + name: true, + applicationStatus: true, + }, + }, + mariadb: { + columns: { + mariadbId: true, + }, + }, + mongo: { + columns: { + mongoId: true, + }, + }, + mysql: { + columns: { + mysqlId: true, + }, + }, + postgres: { + columns: { + postgresId: true, + }, + }, + redis: { + columns: { + redisId: true, }, }, - mariadb: true, - mongo: true, - mysql: true, - postgres: true, - redis: true, compose: { - with: { - domains: true, + columns: { + composeId: true, + name: true, + composeStatus: true, }, }, }, + columns: { + name: true, + environmentId: true, + isDefault: true, + }, }, }, where: eq(projects.organizationId, ctx.session.activeOrganizationId), @@ -277,6 +348,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({ diff --git a/packages/server/src/services/environment.ts b/packages/server/src/services/environment.ts index d37e7b789..9be18a287 100644 --- a/packages/server/src/services/environment.ts +++ b/packages/server/src/services/environment.ts @@ -34,42 +34,139 @@ export const createEnvironment = async ( export const findEnvironmentById = async (environmentId: string) => { const environment = await db.query.environments.findFirst({ where: eq(environments.environmentId, environmentId), + columns: { + name: true, + description: true, + environmentId: true, + isDefault: true, + projectId: true, + env: true, + }, with: { applications: { with: { - deployments: true, - server: true, + server: { + columns: { + name: true, + serverId: true, + }, + }, + }, + columns: { + name: true, + applicationId: true, + createdAt: true, + applicationStatus: true, + description: true, + serverId: true, }, }, mariadb: { with: { - server: true, + server: { + columns: { + name: true, + serverId: true, + }, + }, + }, + columns: { + mariadbId: true, + name: true, + createdAt: true, + applicationStatus: true, + description: true, + serverId: true, }, }, mongo: { with: { - server: true, + server: { + columns: { + name: true, + serverId: true, + }, + }, + }, + columns: { + mongoId: true, + name: true, + createdAt: true, + applicationStatus: true, + description: true, + serverId: true, }, }, mysql: { with: { - server: true, + server: { + columns: { + name: true, + serverId: true, + }, + }, + }, + columns: { + mysqlId: true, + name: true, + createdAt: true, + applicationStatus: true, + description: true, + serverId: true, }, }, postgres: { with: { - server: true, + server: { + columns: { + name: true, + serverId: true, + }, + }, + }, + columns: { + postgresId: true, + name: true, + description: true, + createdAt: true, + applicationStatus: true, + serverId: true, }, }, redis: { with: { - server: true, + server: { + columns: { + name: true, + serverId: true, + }, + }, + }, + columns: { + redisId: true, + name: true, + createdAt: true, + applicationStatus: true, + description: true, + serverId: true, }, }, compose: { with: { - deployments: true, - server: true, + server: { + columns: { + name: true, + serverId: true, + }, + }, + }, + columns: { + composeId: true, + name: true, + createdAt: true, + composeStatus: true, + description: true, + serverId: true, }, }, project: true, @@ -98,6 +195,12 @@ export const findEnvironmentsByProjectId = async (projectId: string) => { compose: true, project: true, }, + columns: { + name: true, + description: true, + environmentId: true, + isDefault: true, + }, }); return projectEnvironments; }; @@ -169,6 +272,7 @@ export const duplicateEnvironment = async ( name: input.name, description: input.description || originalEnvironment.description, projectId: originalEnvironment.projectId, + env: originalEnvironment.env, }) .returning() .then((value) => value[0]);