diff --git a/apps/dokploy/components/dashboard/home/show-home.tsx b/apps/dokploy/components/dashboard/home/show-home.tsx index 2e569af7f..93af9604b 100644 --- a/apps/dokploy/components/dashboard/home/show-home.tsx +++ b/apps/dokploy/components/dashboard/home/show-home.tsx @@ -95,8 +95,7 @@ function StatusListCard({ export const ShowHome = () => { const { data: auth } = api.user.get.useQuery(); - const { data: projects } = api.project.all.useQuery(); - const { data: servers } = api.server.all.useQuery(); + const { data: homeStats } = api.project.homeStats.useQuery(); const { data: permissions } = api.user.getPermissions.useQuery(); const canReadDeployments = !!permissions?.deployment.read; const { data: deployments } = api.deployment.allCentralized.useQuery( @@ -109,53 +108,19 @@ export const ShowHome = () => { const firstName = auth?.user?.firstName?.trim(); - const { totals, statusBreakdown } = useMemo(() => { - let applications = 0; - let compose = 0; - let databases = 0; - let environments = 0; - const breakdown = { running: 0, error: 0, idle: 0 }; - const dbKeys = [ - "postgres", - "mysql", - "mariadb", - "mongo", - "redis", - "libsql", - ] as const; - const bump = (status?: string) => { - if (status === "done") breakdown.running++; - else if (status === "error") breakdown.error++; - else breakdown.idle++; - }; - for (const p of projects ?? []) { - for (const env of p.environments ?? []) { - environments++; - applications += env.applications?.length ?? 0; - compose += env.compose?.length ?? 0; - for (const a of env.applications ?? []) bump(a.applicationStatus); - for (const c of env.compose ?? []) bump((c as any).composeStatus); - for (const key of dbKeys) { - const list = ((env as any)[key] ?? []) as Array<{ - applicationStatus?: string; - }>; - databases += list.length; - for (const s of list) bump(s.applicationStatus); - } - } - } - return { - totals: { - projects: projects?.length ?? 0, - environments, - applications, - compose, - databases, - services: applications + compose + databases, - }, - statusBreakdown: breakdown, - }; - }, [projects, servers]); + const totals = homeStats ?? { + projects: 0, + environments: 0, + applications: 0, + compose: 0, + databases: 0, + services: 0, + }; + const statusBreakdown = homeStats?.status ?? { + running: 0, + error: 0, + idle: 0, + }; const recentDeployments = useMemo(() => { if (!deployments) return []; diff --git a/apps/dokploy/server/api/routers/project.ts b/apps/dokploy/server/api/routers/project.ts index cccd389dd..7383daeb4 100644 --- a/apps/dokploy/server/api/routers/project.ts +++ b/apps/dokploy/server/api/routers/project.ts @@ -487,6 +487,149 @@ export const projectRouter = createTRPCRouter({ }, ), + homeStats: protectedProcedure.query(async ({ ctx }) => { + const isPrivileged = + ctx.user.role === "owner" || ctx.user.role === "admin"; + + let accessedProjects: string[] = []; + let accessedEnvironments: string[] = []; + let accessedServices: string[] = []; + + if (!isPrivileged) { + const member = await findMemberByUserId( + ctx.user.id, + ctx.session.activeOrganizationId, + ); + accessedProjects = member.accessedProjects; + accessedEnvironments = member.accessedEnvironments; + accessedServices = member.accessedServices; + + if (accessedProjects.length === 0) { + return { + projects: 0, + environments: 0, + applications: 0, + compose: 0, + databases: 0, + services: 0, + status: { running: 0, error: 0, idle: 0 }, + }; + } + } + + const projectIdFilter = isPrivileged + ? eq(projects.organizationId, ctx.session.activeOrganizationId) + : and( + sql`${projects.projectId} IN (${sql.join( + accessedProjects.map((id) => sql`${id}`), + sql`, `, + )})`, + eq(projects.organizationId, ctx.session.activeOrganizationId), + ); + + const environmentFilter = isPrivileged + ? undefined + : accessedEnvironments.length === 0 + ? sql`false` + : sql`${environments.environmentId} IN (${sql.join( + accessedEnvironments.map((envId) => sql`${envId}`), + sql`, `, + )})`; + + const applyFilter = (col: AnyPgColumn) => + isPrivileged ? undefined : buildServiceFilter(col, accessedServices); + + const rows = await db.query.projects.findMany({ + where: projectIdFilter, + columns: { projectId: true }, + with: { + environments: { + where: environmentFilter, + columns: { environmentId: true }, + with: { + applications: { + where: applyFilter(applications.applicationId), + columns: { applicationStatus: true }, + }, + compose: { + where: applyFilter(compose.composeId), + columns: { composeStatus: true }, + }, + libsql: { + where: applyFilter(libsql.libsqlId), + columns: { applicationStatus: true }, + }, + mariadb: { + where: applyFilter(mariadb.mariadbId), + columns: { applicationStatus: true }, + }, + mongo: { + where: applyFilter(mongo.mongoId), + columns: { applicationStatus: true }, + }, + mysql: { + where: applyFilter(mysql.mysqlId), + columns: { applicationStatus: true }, + }, + postgres: { + where: applyFilter(postgres.postgresId), + columns: { applicationStatus: true }, + }, + redis: { + where: applyFilter(redis.redisId), + columns: { applicationStatus: true }, + }, + }, + }, + }, + }); + + let applicationsCount = 0; + let composeCount = 0; + let databasesCount = 0; + let environmentsCount = 0; + const status = { running: 0, error: 0, idle: 0 }; + const bump = (s?: string | null) => { + if (s === "done") status.running++; + else if (s === "error") status.error++; + else status.idle++; + }; + + for (const project of rows) { + for (const env of project.environments) { + environmentsCount++; + applicationsCount += env.applications.length; + composeCount += env.compose.length; + databasesCount += + env.libsql.length + + env.mariadb.length + + env.mongo.length + + env.mysql.length + + env.postgres.length + + env.redis.length; + + for (const a of env.applications) bump(a.applicationStatus); + for (const c of env.compose) bump(c.composeStatus); + for (const s of env.libsql) bump(s.applicationStatus); + for (const s of env.mariadb) bump(s.applicationStatus); + for (const s of env.mongo) bump(s.applicationStatus); + for (const s of env.mysql) bump(s.applicationStatus); + for (const s of env.postgres) bump(s.applicationStatus); + for (const s of env.redis) bump(s.applicationStatus); + } + } + + return { + projects: rows.length, + environments: environmentsCount, + applications: applicationsCount, + compose: composeCount, + databases: databasesCount, + services: applicationsCount + composeCount + databasesCount, + status, + }; + }), + search: protectedProcedure .input( z.object({