mirror of
https://github.com/dokploy/dokploy.git
synced 2026-06-13 19:09:49 +00:00
feat: implement homeStats query for dashboard overview
- Replace individual project and server queries with a consolidated homeStats query to streamline data retrieval for the dashboard. - Update the ShowHome component to utilize homeStats for displaying project, environment, application, and service counts, along with their status breakdown. - Enhance data handling for user permissions to ensure accurate statistics based on user access levels.
This commit is contained in:
@@ -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 [];
|
||||
|
||||
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user