Update DNS metrics charts and API to include dynamic date ranges and improved data fetching.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 9111ef36-26c8-4085-84ca-a35dc1fec1b5
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7083d608-d6d3-4a6a-9a27-6286c5109627/288dfb12-98b8-4936-9b81-c64cee571edd.jpg
This commit is contained in:
alphaeusmote
2025-04-10 02:25:07 +00:00
3 changed files with 243 additions and 24 deletions
+125 -18
View File
@@ -14,68 +14,175 @@ import {
ResponsiveContainer,
Legend,
} from "recharts";
import { DnsMetric } from "@shared/schema";
interface DnsUpdateChartProps {
timeframe?: string;
domainId?: number;
domainId?: string;
}
interface ChartDataPoint {
time: string;
updates: number;
}
export function DnsUpdateChart({
timeframe = "day",
domainId
}: DnsUpdateChartProps) {
// This would be a real API query in a production app
// For this MVP, we'll generate sample data
// Calculate the date range based on timeframe
const getDateRange = () => {
const endDate = new Date();
let startDate = new Date();
if (timeframe === "day") {
startDate.setDate(startDate.getDate() - 1);
} else if (timeframe === "week") {
startDate.setDate(startDate.getDate() - 7);
} else if (timeframe === "month") {
startDate.setMonth(startDate.getMonth() - 1);
}
return {
startDate: startDate.toISOString(),
endDate: endDate.toISOString()
};
};
const { startDate, endDate } = getDateRange();
// Create a query key that includes the timeframe and domainId
const queryKey = ["/api/metrics/dns-updates", timeframe, domainId];
const queryKey = domainId ?
["/api/dns-metrics/domain", domainId, timeframe, startDate, endDate] :
["/api/dns-metrics", timeframe, startDate, endDate];
// In a real app, this would fetch data from the API
const { data: chartData, isLoading } = useQuery({
// Use the actual DNS metrics API endpoint
const { data: metricsData, isLoading } = useQuery<DnsMetric[]>({
queryKey,
queryFn: async () => generateSampleData(timeframe),
queryFn: async () => {
const baseUrl = domainId ?
`/api/dns-metrics/domain/${domainId}` :
"/api/dns-metrics";
const url = new URL(baseUrl, window.location.origin);
url.searchParams.append("type", "update");
url.searchParams.append("startDate", startDate);
url.searchParams.append("endDate", endDate);
const response = await fetch(url.toString());
if (!response.ok) {
throw new Error("Failed to fetch DNS metrics");
}
return response.json();
},
// Keep data fresh for 5 minutes
staleTime: 5 * 60 * 1000,
});
// Generate sample data based on timeframe
// In a real application, this would come from the API
const generateSampleData = (timeframe: string) => {
const data = [];
// Transform the metrics data into chart format
const transformMetricsToChartData = (metrics: DnsMetric[] | undefined): ChartDataPoint[] => {
if (!metrics || metrics.length === 0) {
return generateFallbackData(timeframe);
}
// Group metrics by time period (hour, day, or week)
const groupedData = new Map<string, number>();
metrics.forEach((metric) => {
const date = new Date(metric.timestamp);
let timeKey: string;
if (timeframe === "day") {
// Group by hour
timeKey = `${date.getHours().toString().padStart(2, "0")}:00`;
} else if (timeframe === "week") {
// Group by day
const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
timeKey = days[date.getDay()];
} else {
// Group by week for month view
const weekOfMonth = Math.ceil((date.getDate() + (new Date(date.getFullYear(), date.getMonth(), 1).getDay())) / 7);
timeKey = `Week ${weekOfMonth}`;
}
// Increment the count for this time period
groupedData.set(timeKey, (groupedData.get(timeKey) || 0) + 1);
});
// Convert the map to an array of data points
const result: ChartDataPoint[] = [];
if (timeframe === "day") {
// Ensure all 24 hours are represented
for (let i = 0; i < 24; i++) {
const hour = `${i.toString().padStart(2, "0")}:00`;
result.push({
time: hour,
updates: groupedData.get(hour) || 0
});
}
} else if (timeframe === "week") {
// Ensure all 7 days are represented
const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
days.forEach(day => {
result.push({
time: day,
updates: groupedData.get(day) || 0
});
});
} else {
// For month, ensure all weeks are represented
for (let i = 1; i <= 4; i++) {
const week = `Week ${i}`;
result.push({
time: week,
updates: groupedData.get(week) || 0
});
}
}
return result;
};
// Generate fallback data when no metrics are available
const generateFallbackData = (timeframe: string): ChartDataPoint[] => {
const data: ChartDataPoint[] = [];
if (timeframe === "day") {
// Generate hourly data for a day
for (let i = 0; i < 24; i++) {
const hour = i.toString().padStart(2, "0") + ":00";
const updateCount = Math.floor(Math.random() * 10) + 1;
data.push({
time: hour,
updates: updateCount,
updates: 0,
});
}
} else if (timeframe === "week") {
// Generate daily data for a week
const days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
for (let i = 0; i < 7; i++) {
const updateCount = Math.floor(Math.random() * 50) + 10;
data.push({
time: days[i],
updates: updateCount,
updates: 0,
});
}
} else if (timeframe === "month") {
// Generate weekly data for a month
for (let i = 1; i <= 4; i++) {
const updateCount = Math.floor(Math.random() * 200) + 50;
data.push({
time: `Week ${i}`,
updates: updateCount,
updates: 0,
});
}
}
return data;
};
// Process the metrics data for the chart
const chartData = transformMetricsToChartData(metricsData);
if (isLoading) {
return (
+41 -6
View File
@@ -4,7 +4,7 @@ import { MainLayout } from "@/components/layouts/main-layout";
import { DnsUpdateChart } from "@/components/charts/dns-update-chart";
import { ProviderDistributionChart } from "@/components/charts/provider-distribution-chart";
import { RecordTypeChart } from "@/components/charts/record-type-chart";
import { Domain, DnsRecord } from "@shared/schema";
import { Domain, DnsMetric } from "@shared/schema";
import { useOrganization } from "@/context/organization-context";
import {
Card,
@@ -39,10 +39,45 @@ export default function MetricsPage() {
enabled: !!currentOrganization?.id,
});
// Fetch DNS records stats (would be from actual API)
const { data: recordsStats } = useQuery({
queryKey: ["/api/metrics/records", selectedTimeframe, selectedDomain],
enabled: false, // Would be enabled in a real implementation
// Calculate date range for metrics queries
const getDateRange = () => {
const endDate = new Date();
let startDate = new Date();
if (selectedTimeframe === "day") {
startDate.setDate(startDate.getDate() - 1);
} else if (selectedTimeframe === "week") {
startDate.setDate(startDate.getDate() - 7);
} else if (selectedTimeframe === "month") {
startDate.setMonth(startDate.getMonth() - 1);
}
return {
startDate: startDate.toISOString(),
endDate: endDate.toISOString()
};
};
const { startDate, endDate } = getDateRange();
// Fetch DNS metrics for the performance cards
const { data: performanceMetrics = [] } = useQuery<DnsMetric[]>({
queryKey: ["/api/dns-metrics", "performance", startDate, endDate],
queryFn: async () => {
const url = new URL("/api/dns-metrics", window.location.origin);
url.searchParams.append("type", "performance");
url.searchParams.append("startDate", startDate);
url.searchParams.append("endDate", endDate);
const response = await fetch(url.toString());
if (!response.ok) {
throw new Error("Failed to fetch performance metrics");
}
return response.json();
},
staleTime: 5 * 60 * 1000, // 5 minutes
});
return (
@@ -108,7 +143,7 @@ export default function MetricsPage() {
</CardHeader>
<CardContent>
<div className="h-80">
<DnsUpdateChart timeframe={selectedTimeframe} domainId={selectedDomain !== "all" ? parseInt(selectedDomain) : undefined} />
<DnsUpdateChart timeframe={selectedTimeframe} domainId={selectedDomain !== "all" ? selectedDomain : undefined} />
</div>
</CardContent>
</Card>
+77
View File
@@ -743,6 +743,83 @@ export async function registerRoutes(app: Express): Promise<Server> {
}
});
// DNS Metrics
app.get("/api/dns-metrics", requireRole(["admin", "manager", "user", "readonly"]), async (req, res) => {
try {
const metricType = req.query.type ? req.query.type as string : undefined;
const startDate = req.query.startDate ? new Date(req.query.startDate as string) : undefined;
const endDate = req.query.endDate ? new Date(req.query.endDate as string) : undefined;
const metrics = await storage.getDnsMetricsByType(
metricType || "all",
startDate,
endDate
);
res.json(metrics);
} catch (error) {
console.error("Error fetching DNS metrics:", error);
res.status(500).json({ message: "Internal server error" });
}
});
app.get("/api/dns-metrics/domain/:domainId", requireRole(["admin", "manager", "user", "readonly"]), async (req, res) => {
try {
const domainId = req.params.domainId;
const metricType = req.query.type ? req.query.type as string : undefined;
const startDate = req.query.startDate ? new Date(req.query.startDate as string) : undefined;
const endDate = req.query.endDate ? new Date(req.query.endDate as string) : undefined;
const metrics = await storage.getDnsMetricsByDomain(
domainId,
metricType,
startDate,
endDate
);
res.json(metrics);
} catch (error) {
console.error("Error fetching domain DNS metrics:", error);
res.status(500).json({ message: "Internal server error" });
}
});
app.get("/api/dns-metrics/record/:recordId", requireRole(["admin", "manager", "user", "readonly"]), async (req, res) => {
try {
const recordId = req.params.recordId;
const metricType = req.query.type ? req.query.type as string : undefined;
const startDate = req.query.startDate ? new Date(req.query.startDate as string) : undefined;
const endDate = req.query.endDate ? new Date(req.query.endDate as string) : undefined;
const metrics = await storage.getDnsMetricsByRecord(
recordId,
metricType,
startDate,
endDate
);
res.json(metrics);
} catch (error) {
console.error("Error fetching record DNS metrics:", error);
res.status(500).json({ message: "Internal server error" });
}
});
app.post("/api/dns-metrics", requireRole(["admin", "manager"]), async (req, res) => {
try {
const validatedData = insertDnsMetricSchema.parse(req.body);
const metric = await storage.addDnsMetric(validatedData);
res.status(201).json(metric);
} catch (error) {
if (error instanceof z.ZodError) {
res.status(400).json({ message: "Validation error", errors: error.errors });
} else {
console.error("Error creating DNS metric:", error);
res.status(500).json({ message: "Internal server error" });
}
}
});
// Webhooks
app.get("/api/webhooks", requireRole(["admin", "manager"]), async (req, res) => {
try {