mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-04 16:26:16 +00:00
Minor improvements on offline usage report
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { TableName } from "@app/db/schemas";
|
import { ProjectType, TableName } from "@app/db/schemas";
|
||||||
|
|
||||||
export type TOfflineUsageReportDALFactory = ReturnType<typeof offlineUsageReportDALFactory>;
|
export type TOfflineUsageReportDALFactory = ReturnType<typeof offlineUsageReportDALFactory>;
|
||||||
|
|
||||||
@@ -110,6 +110,7 @@ export const offlineUsageReportDALFactory = (db: TDbClient) => {
|
|||||||
.leftJoin(`${TableName.SecretFolder} as sf`, "s.folderId", "sf.id")
|
.leftJoin(`${TableName.SecretFolder} as sf`, "s.folderId", "sf.id")
|
||||||
.leftJoin(`${TableName.Environment} as e`, "sf.envId", "e.id")
|
.leftJoin(`${TableName.Environment} as e`, "sf.envId", "e.id")
|
||||||
.leftJoin(`${TableName.Project} as p`, "e.projectId", "p.id")
|
.leftJoin(`${TableName.Project} as p`, "e.projectId", "p.id")
|
||||||
|
.where("p.type", ProjectType.SecretManager)
|
||||||
.groupBy("p.id")
|
.groupBy("p.id")
|
||||||
.whereNotNull("p.id")) as Array<{ projectId: string; count: string }>;
|
.whereNotNull("p.id")) as Array<{ projectId: string; count: string }>;
|
||||||
|
|
||||||
@@ -144,6 +145,7 @@ export const offlineUsageReportDALFactory = (db: TDbClient) => {
|
|||||||
.leftJoin(`${TableName.SecretFolder} as sf`, "s.folderId", "sf.id")
|
.leftJoin(`${TableName.SecretFolder} as sf`, "s.folderId", "sf.id")
|
||||||
.leftJoin(`${TableName.Environment} as e`, "sf.envId", "e.id")
|
.leftJoin(`${TableName.Environment} as e`, "sf.envId", "e.id")
|
||||||
.leftJoin(`${TableName.Project} as p`, "e.projectId", "p.id")
|
.leftJoin(`${TableName.Project} as p`, "e.projectId", "p.id")
|
||||||
|
.where("p.type", ProjectType.SecretManager)
|
||||||
.groupBy("p.id", "p.name")
|
.groupBy("p.id", "p.name")
|
||||||
.whereNotNull("p.id")) as Array<{ projectId: string; projectName: string; secretCount: string }>;
|
.whereNotNull("p.id")) as Array<{ projectId: string; projectName: string; secretCount: string }>;
|
||||||
|
|
||||||
|
|||||||
@@ -4,3 +4,15 @@ export const downloadTxtFile = (filename: string, content: string) => {
|
|||||||
const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
|
const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
|
||||||
FileSaver.saveAs(blob, filename);
|
FileSaver.saveAs(blob, filename);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const downloadFile = (content: string, filename: string, mimeType: string = "text/csv") => {
|
||||||
|
const blob = new Blob([content], { type: mimeType });
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = url;
|
||||||
|
link.download = filename;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,32 +1,18 @@
|
|||||||
import { useState } from "react";
|
|
||||||
import { faDownload, faFileAlt, faSpinner } from "@fortawesome/free-solid-svg-icons";
|
import { faDownload, faFileAlt, faSpinner } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, Card, CardTitle } from "@app/components/v2";
|
import { Button, Card, CardTitle } from "@app/components/v2";
|
||||||
import { apiRequest } from "@app/config/request";
|
import { downloadFile } from "@app/helpers/download";
|
||||||
|
import { useGenerateUsageReport } from "@app/hooks/api/admin/mutation";
|
||||||
|
|
||||||
export const UsageReportSection = () => {
|
export const UsageReportSection = () => {
|
||||||
const [isGenerating, setIsGenerating] = useState(false);
|
const generateUsageReport = useGenerateUsageReport();
|
||||||
|
|
||||||
const downloadFile = (content: string, filename: string, mimeType: string = "text/csv") => {
|
|
||||||
const blob = new Blob([content], { type: mimeType });
|
|
||||||
const url = window.URL.createObjectURL(blob);
|
|
||||||
const link = document.createElement("a");
|
|
||||||
link.href = url;
|
|
||||||
link.download = filename;
|
|
||||||
document.body.appendChild(link);
|
|
||||||
link.click();
|
|
||||||
document.body.removeChild(link);
|
|
||||||
window.URL.revokeObjectURL(url);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleGenerateReport = async () => {
|
const handleGenerateReport = async () => {
|
||||||
try {
|
try {
|
||||||
setIsGenerating(true);
|
const response = await generateUsageReport.mutateAsync();
|
||||||
|
const { csvContent, filename } = response;
|
||||||
const response = await apiRequest.post("/api/v1/admin/usage-report/generate", {});
|
|
||||||
const { csvContent, filename } = response.data;
|
|
||||||
|
|
||||||
downloadFile(csvContent, filename, "text/csv");
|
downloadFile(csvContent, filename, "text/csv");
|
||||||
|
|
||||||
@@ -40,8 +26,6 @@ export const UsageReportSection = () => {
|
|||||||
text: "Failed to generate usage report. Please try again.",
|
text: "Failed to generate usage report. Please try again.",
|
||||||
type: "error"
|
type: "error"
|
||||||
});
|
});
|
||||||
} finally {
|
|
||||||
setIsGenerating(false);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -53,19 +37,16 @@ export const UsageReportSection = () => {
|
|||||||
</CardTitle>
|
</CardTitle>
|
||||||
|
|
||||||
<div className="mb-4 text-sm text-gray-400">
|
<div className="mb-4 text-sm text-gray-400">
|
||||||
Generate tamper-proof, cryptographically signed usage reports to ensure offline license
|
Generate secure usage reports for offline license compliance and billing verification.
|
||||||
compliance and accurate billing verification. Reports capture user counts, machine
|
|
||||||
identities, project details, and secrets metadata, providing a secure and verifiable audit
|
|
||||||
trail.
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
onClick={handleGenerateReport}
|
onClick={handleGenerateReport}
|
||||||
className="w-fit"
|
className="w-fit"
|
||||||
isLoading={isGenerating}
|
isLoading={generateUsageReport.isPending}
|
||||||
leftIcon={<FontAwesomeIcon icon={isGenerating ? faSpinner : faDownload} />}
|
leftIcon={<FontAwesomeIcon icon={generateUsageReport.isPending ? faSpinner : faDownload} />}
|
||||||
>
|
>
|
||||||
{isGenerating ? "Generating..." : "Generate Report"}
|
{generateUsageReport.isPending ? "Generating..." : "Generate Report"}
|
||||||
</Button>
|
</Button>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user