From 14473c742c18d63b8eae2644073d2d3512088e0b Mon Sep 17 00:00:00 2001 From: Carlos Monastyrski Date: Mon, 1 Sep 2025 21:18:48 -0300 Subject: [PATCH] Address greptile comments --- backend/src/server/routes/v1/admin-router.ts | 7 ---- .../offline-usage-report-dal.ts | 18 ++++++---- .../offline-usage-report-service.ts | 11 ++---- frontend/src/hooks/api/admin/mutation.ts | 8 ++--- frontend/src/hooks/api/admin/types.ts | 4 --- .../components/UsageReportSection.tsx | 35 +++++-------------- 6 files changed, 26 insertions(+), 57 deletions(-) diff --git a/backend/src/server/routes/v1/admin-router.ts b/backend/src/server/routes/v1/admin-router.ts index d1a783b8d..0b19906ba 100644 --- a/backend/src/server/routes/v1/admin-router.ts +++ b/backend/src/server/routes/v1/admin-router.ts @@ -863,13 +863,6 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { }); }, handler: async () => { - const cfg = getConfig(); - if (!cfg.LICENSE_KEY_OFFLINE) { - throw new BadRequestError({ - message: "Offline usage reports are not enabled. LICENSE_KEY_OFFLINE must be configured." - }); - } - const result = await server.services.offlineUsageReport.generateUsageReportCSV(); return { diff --git a/backend/src/services/offline-usage-report/offline-usage-report-dal.ts b/backend/src/services/offline-usage-report/offline-usage-report-dal.ts index 98bdcd732..d979887ef 100644 --- a/backend/src/services/offline-usage-report/offline-usage-report-dal.ts +++ b/backend/src/services/offline-usage-report/offline-usage-report-dal.ts @@ -104,17 +104,21 @@ export const offlineUsageReportDALFactory = (db: TDbClient) => { // Calculate average secrets per project const secretsPerProject = (await db - .from(TableName.SecretV2) - .select("folderId") - .count("* as count") - .groupBy("folderId")) as Array<{ folderId: string; count: string }>; + .from(`${TableName.SecretV2} as s`) + .select("p.id as projectId") + .count("s.id as count") + .leftJoin(`${TableName.SecretFolder} as sf`, "s.folderId", "sf.id") + .leftJoin(`${TableName.Environment} as e`, "sf.envId", "e.id") + .leftJoin(`${TableName.Project} as p`, "e.projectId", "p.id") + .groupBy("p.id") + .whereNotNull("p.id")) as Array<{ projectId: string; count: string }>; const averageSecretsPerProject = - totalProjects > 0 + secretsPerProject.length > 0 ? secretsPerProject.reduce( - (sum, row: { folderId: string; count: string }) => sum + parseInt(row.count, 10), + (sum, row: { projectId: string; count: string }) => sum + parseInt(row.count, 10), 0 - ) / totalProjects + ) / secretsPerProject.length : 0; return { diff --git a/backend/src/services/offline-usage-report/offline-usage-report-service.ts b/backend/src/services/offline-usage-report/offline-usage-report-service.ts index 492fd7aae..179232aa4 100644 --- a/backend/src/services/offline-usage-report/offline-usage-report-service.ts +++ b/backend/src/services/offline-usage-report/offline-usage-report-service.ts @@ -58,8 +58,6 @@ export const offlineUsageReportServiceFactory = ({ offlineUsageReportDAL.getSecretRotationMetrics() ]); - const csvLines: string[] = []; - const headers = [ "Total Users", "Admin Users", @@ -89,8 +87,6 @@ export const offlineUsageReportServiceFactory = ({ headers.push("Signature"); - csvLines.push(headers.slice(0, -1).join(",")); - const dataRow: (string | number)[] = [ userMetrics.totalUsers, userMetrics.adminUsers, @@ -114,14 +110,13 @@ export const offlineUsageReportServiceFactory = ({ dataRow.push(projectMetrics.projectsByType[type] || 0); }); - const tempCsvLines = [headers.slice(0, -1).join(","), dataRow.join(",")]; - const contentWithoutSignature = tempCsvLines.join("\n"); + const headersWithoutSignature = headers.slice(0, -1); + const contentWithoutSignature = [headersWithoutSignature.join(","), dataRow.join(",")].join("\n"); const signature = signReportContent(contentWithoutSignature, licenseId); dataRow.push(signature); - const finalCsvLines = [headers.join(","), dataRow.join(",")]; - const csvContent = finalCsvLines.join("\n"); + const csvContent = [headers.join(","), dataRow.join(",")].join("\n"); return { csvContent, diff --git a/frontend/src/hooks/api/admin/mutation.ts b/frontend/src/hooks/api/admin/mutation.ts index 89c9f8586..5ea351c34 100644 --- a/frontend/src/hooks/api/admin/mutation.ts +++ b/frontend/src/hooks/api/admin/mutation.ts @@ -8,7 +8,6 @@ import { adminQueryKeys, adminStandaloneKeys } from "./queries"; import { RootKeyEncryptionStrategy, TCreateAdminUserDTO, - TGenerateUsageReportDTO, TInvalidateCacheDTO, TServerConfig, TUpdateServerConfigDTO, @@ -197,11 +196,10 @@ export const useInvalidateCache = () => { }; export const useGenerateUsageReport = () => { - return useMutation({ - mutationFn: async (dto) => { + return useMutation({ + mutationFn: async () => { const { data } = await apiRequest.post( - "/api/v1/admin/usage-report/generate", - dto + "/api/v1/admin/usage-report/generate" ); return data; } diff --git a/frontend/src/hooks/api/admin/types.ts b/frontend/src/hooks/api/admin/types.ts index 1c46d402b..769aaccfc 100644 --- a/frontend/src/hooks/api/admin/types.ts +++ b/frontend/src/hooks/api/admin/types.ts @@ -144,10 +144,6 @@ export interface TGetEnvOverrides { }; } -export type TGenerateUsageReportDTO = { - periodDays?: number; -}; - export type TUsageReportResponse = { filename: string; csvContent: string; diff --git a/frontend/src/pages/admin/GeneralPage/components/UsageReportSection.tsx b/frontend/src/pages/admin/GeneralPage/components/UsageReportSection.tsx index 5ebc45939..21e4da557 100644 --- a/frontend/src/pages/admin/GeneralPage/components/UsageReportSection.tsx +++ b/frontend/src/pages/admin/GeneralPage/components/UsageReportSection.tsx @@ -1,28 +1,14 @@ import { useState } from "react"; -import { useForm } from "react-hook-form"; import { faDownload, faFileAlt, faSpinner } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { z } from "zod"; import { createNotification } from "@app/components/notifications"; import { Button, Card, CardTitle } from "@app/components/v2"; import { apiRequest } from "@app/config/request"; -const formSchema = z.object({}); - -type TUsageReportForm = z.infer; - export const UsageReportSection = () => { const [isGenerating, setIsGenerating] = useState(false); - const { - handleSubmit, - formState: { isSubmitting } - } = useForm({ - resolver: zodResolver(formSchema) - }); - const downloadFile = (content: string, filename: string, mimeType: string = "text/csv") => { const blob = new Blob([content], { type: mimeType }); const url = window.URL.createObjectURL(blob); @@ -35,7 +21,7 @@ export const UsageReportSection = () => { window.URL.revokeObjectURL(url); }; - const onSubmit = async () => { + const handleGenerateReport = async () => { try { setIsGenerating(true); @@ -59,8 +45,6 @@ export const UsageReportSection = () => { } }; - const isLoading = isSubmitting || isGenerating; - return ( @@ -75,15 +59,14 @@ export const UsageReportSection = () => { trail. -
- -
+
); };