mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-05 10:16:41 +00:00
♻️ set default log analytics time range to. 7days ago
This commit is contained in:
@@ -2,7 +2,7 @@ import { db, requestAuditLog, driver } from "@server/db";
|
|||||||
import { registry } from "@server/openApi";
|
import { registry } from "@server/openApi";
|
||||||
import { NextFunction } from "express";
|
import { NextFunction } from "express";
|
||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { eq, gt, lt, and, count, sql, desc, not, isNull } from "drizzle-orm";
|
import { eq, gte, lte, and, count, sql, desc, not, isNull } from "drizzle-orm";
|
||||||
import { OpenAPITags } from "@server/openApi";
|
import { OpenAPITags } from "@server/openApi";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
@@ -11,6 +11,14 @@ import { fromError } from "zod-validation-error";
|
|||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
|
|
||||||
|
function getSevenDaysAgo() {
|
||||||
|
const today = new Date();
|
||||||
|
today.setHours(0, 0, 0, 0); // Set to midnight
|
||||||
|
const sevenDaysAgo = new Date(today);
|
||||||
|
sevenDaysAgo.setDate(today.getDate() - 7);
|
||||||
|
return sevenDaysAgo.toISOString();
|
||||||
|
}
|
||||||
|
|
||||||
const queryAccessAuditLogsQuery = z.object({
|
const queryAccessAuditLogsQuery = z.object({
|
||||||
// iso string just validate its a parseable date
|
// iso string just validate its a parseable date
|
||||||
timeStart: z
|
timeStart: z
|
||||||
@@ -19,7 +27,8 @@ const queryAccessAuditLogsQuery = z.object({
|
|||||||
error: "timeStart must be a valid ISO date string"
|
error: "timeStart must be a valid ISO date string"
|
||||||
})
|
})
|
||||||
.transform((val) => Math.floor(new Date(val).getTime() / 1000))
|
.transform((val) => Math.floor(new Date(val).getTime() / 1000))
|
||||||
.optional(),
|
.optional()
|
||||||
|
.prefault(getSevenDaysAgo),
|
||||||
timeEnd: z
|
timeEnd: z
|
||||||
.string()
|
.string()
|
||||||
.refine((val) => !isNaN(Date.parse(val)), {
|
.refine((val) => !isNaN(Date.parse(val)), {
|
||||||
@@ -55,15 +64,10 @@ type Q = z.infer<typeof queryRequestAuditLogsCombined>;
|
|||||||
async function query(query: Q) {
|
async function query(query: Q) {
|
||||||
let baseConditions = and(
|
let baseConditions = and(
|
||||||
eq(requestAuditLog.orgId, query.orgId),
|
eq(requestAuditLog.orgId, query.orgId),
|
||||||
lt(requestAuditLog.timestamp, query.timeEnd)
|
gte(requestAuditLog.timestamp, query.timeStart),
|
||||||
|
lte(requestAuditLog.timestamp, query.timeEnd)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (query.timeStart) {
|
|
||||||
baseConditions = and(
|
|
||||||
baseConditions,
|
|
||||||
gt(requestAuditLog.timestamp, query.timeStart)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (query.resourceId) {
|
if (query.resourceId) {
|
||||||
baseConditions = and(
|
baseConditions = and(
|
||||||
baseConditions,
|
baseConditions,
|
||||||
|
|||||||
@@ -1,22 +1,27 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
import { cn } from "@app/lib/cn";
|
||||||
import { createApiClient } from "@app/lib/api";
|
|
||||||
import {
|
import {
|
||||||
logAnalyticsFiltersSchema,
|
logAnalyticsFiltersSchema,
|
||||||
logQueries,
|
logQueries,
|
||||||
resourceQueries
|
resourceQueries
|
||||||
} from "@app/lib/queries";
|
} from "@app/lib/queries";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
|
||||||
import { useState } from "react";
|
|
||||||
import { Card, CardContent, CardHeader } from "./ui/card";
|
|
||||||
import { LoaderIcon, RefreshCw, XIcon } from "lucide-react";
|
import { LoaderIcon, RefreshCw, XIcon } from "lucide-react";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
||||||
import { DateRangePicker, type DateTimeValue } from "./DateTimePicker";
|
import { DateRangePicker, type DateTimeValue } from "./DateTimePicker";
|
||||||
import { Button } from "./ui/button";
|
import { Button } from "./ui/button";
|
||||||
import { cn } from "@app/lib/cn";
|
import { Card, CardContent, CardHeader } from "./ui/card";
|
||||||
import { useTranslations } from "next-intl";
|
|
||||||
|
|
||||||
|
import { countryCodeToFlagEmoji } from "@app/lib/countryCodeToFlagEmoji";
|
||||||
|
import {
|
||||||
|
InfoSection,
|
||||||
|
InfoSectionContent,
|
||||||
|
InfoSections,
|
||||||
|
InfoSectionTitle
|
||||||
|
} from "./InfoSection";
|
||||||
|
import { Label } from "./ui/label";
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
@@ -24,23 +29,10 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue
|
SelectValue
|
||||||
} from "./ui/select";
|
} from "./ui/select";
|
||||||
import { Label } from "./ui/label";
|
|
||||||
import { Separator } from "./ui/separator";
|
import { Separator } from "./ui/separator";
|
||||||
import {
|
|
||||||
InfoSection,
|
|
||||||
InfoSectionContent,
|
|
||||||
InfoSections,
|
|
||||||
InfoSectionTitle
|
|
||||||
} from "./InfoSection";
|
|
||||||
import { WorldMap } from "./WorldMap";
|
import { WorldMap } from "./WorldMap";
|
||||||
import { countryCodeToFlagEmoji } from "@app/lib/countryCodeToFlagEmoji";
|
|
||||||
|
|
||||||
import {
|
import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts";
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipProvider,
|
|
||||||
TooltipTrigger
|
|
||||||
} from "./ui/tooltip";
|
|
||||||
import {
|
import {
|
||||||
ChartContainer,
|
ChartContainer,
|
||||||
ChartLegend,
|
ChartLegend,
|
||||||
@@ -49,12 +41,25 @@ import {
|
|||||||
ChartTooltipContent,
|
ChartTooltipContent,
|
||||||
type ChartConfig
|
type ChartConfig
|
||||||
} from "./ui/chart";
|
} from "./ui/chart";
|
||||||
import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts";
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipProvider,
|
||||||
|
TooltipTrigger
|
||||||
|
} from "./ui/tooltip";
|
||||||
|
|
||||||
export type AnalyticsContentProps = {
|
export type AnalyticsContentProps = {
|
||||||
orgId: string;
|
orgId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getSevenDaysAgo() {
|
||||||
|
const today = new Date();
|
||||||
|
today.setHours(0, 0, 0, 0); // Set to midnight
|
||||||
|
const sevenDaysAgo = new Date(today);
|
||||||
|
sevenDaysAgo.setDate(today.getDate() - 7);
|
||||||
|
return sevenDaysAgo;
|
||||||
|
}
|
||||||
|
|
||||||
export function LogAnalyticsData(props: AnalyticsContentProps) {
|
export function LogAnalyticsData(props: AnalyticsContentProps) {
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const path = usePathname();
|
const path = usePathname();
|
||||||
@@ -67,17 +72,18 @@ export function LogAnalyticsData(props: AnalyticsContentProps) {
|
|||||||
const isEmptySearchParams =
|
const isEmptySearchParams =
|
||||||
!filters.resourceId && !filters.timeStart && !filters.timeEnd;
|
!filters.resourceId && !filters.timeStart && !filters.timeEnd;
|
||||||
|
|
||||||
const env = useEnvContext();
|
|
||||||
const [api] = useState(() => createApiClient(env));
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
console.log({ filters });
|
||||||
const dateRange = {
|
const dateRange = {
|
||||||
startDate: filters.timeStart ? new Date(filters.timeStart) : undefined,
|
startDate: filters.timeStart
|
||||||
endDate: filters.timeEnd ? new Date(filters.timeEnd) : undefined
|
? new Date(filters.timeStart)
|
||||||
|
: getSevenDaysAgo(),
|
||||||
|
endDate: filters.timeEnd ? new Date(filters.timeEnd) : new Date()
|
||||||
};
|
};
|
||||||
|
|
||||||
const { data: resources = [], isFetching: isFetchingResources } = useQuery(
|
const { data: resources = [], isFetching: isFetchingResources } = useQuery(
|
||||||
resourceQueries.listNamesPerOrg(props.orgId, api)
|
resourceQueries.listNamesPerOrg(props.orgId)
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -88,7 +94,6 @@ export function LogAnalyticsData(props: AnalyticsContentProps) {
|
|||||||
} = useQuery(
|
} = useQuery(
|
||||||
logQueries.requestAnalytics({
|
logQueries.requestAnalytics({
|
||||||
orgId: props.orgId,
|
orgId: props.orgId,
|
||||||
api,
|
|
||||||
filters
|
filters
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -168,17 +168,15 @@ export type LogAnalyticsFilters = z.TypeOf<typeof logAnalyticsFiltersSchema>;
|
|||||||
export const logQueries = {
|
export const logQueries = {
|
||||||
requestAnalytics: ({
|
requestAnalytics: ({
|
||||||
orgId,
|
orgId,
|
||||||
filters,
|
filters
|
||||||
api
|
|
||||||
}: {
|
}: {
|
||||||
orgId: string;
|
orgId: string;
|
||||||
filters: LogAnalyticsFilters;
|
filters: LogAnalyticsFilters;
|
||||||
api: AxiosInstance;
|
|
||||||
}) =>
|
}) =>
|
||||||
queryOptions({
|
queryOptions({
|
||||||
queryKey: ["REQUEST_LOG_ANALYTICS", orgId, filters] as const,
|
queryKey: ["REQUEST_LOG_ANALYTICS", orgId, filters] as const,
|
||||||
queryFn: async ({ signal }) => {
|
queryFn: async ({ signal, meta }) => {
|
||||||
const res = await api.get<
|
const res = await meta!.api.get<
|
||||||
AxiosResponse<QueryRequestAnalyticsResponse>
|
AxiosResponse<QueryRequestAnalyticsResponse>
|
||||||
>(`/org/${orgId}/logs/analytics`, {
|
>(`/org/${orgId}/logs/analytics`, {
|
||||||
params: filters,
|
params: filters,
|
||||||
@@ -228,11 +226,11 @@ export const resourceQueries = {
|
|||||||
return res.data.data.clients;
|
return res.data.data.clients;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
listNamesPerOrg: (orgId: string, api: AxiosInstance) =>
|
listNamesPerOrg: (orgId: string) =>
|
||||||
queryOptions({
|
queryOptions({
|
||||||
queryKey: ["RESOURCES_NAMES", orgId] as const,
|
queryKey: ["RESOURCES_NAMES", orgId] as const,
|
||||||
queryFn: async ({ signal }) => {
|
queryFn: async ({ signal, meta }) => {
|
||||||
const res = await api.get<
|
const res = await meta!.api.get<
|
||||||
AxiosResponse<ListResourceNamesResponse>
|
AxiosResponse<ListResourceNamesResponse>
|
||||||
>(`/org/${orgId}/resource-names`, {
|
>(`/org/${orgId}/resource-names`, {
|
||||||
signal
|
signal
|
||||||
|
|||||||
Reference in New Issue
Block a user