analytics page
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { authRouter } from "./routers/auth";
|
||||
import { auditRouter } from "./routers/audit";
|
||||
import { analyticsRouter } from "./routers/analytics";
|
||||
import { databaseRouter } from "./routers/database";
|
||||
import { lineageRouter } from "./routers/lineage";
|
||||
import { miscRouter } from "./routers/misc";
|
||||
@@ -13,6 +14,7 @@ import { createTRPCRouter } from "./utils";
|
||||
export const appRouter = createTRPCRouter({
|
||||
auth: authRouter,
|
||||
audit: auditRouter,
|
||||
analytics: analyticsRouter,
|
||||
database: databaseRouter,
|
||||
lineage: lineageRouter,
|
||||
misc: miscRouter,
|
||||
|
||||
86
src/server/api/routers/analytics.ts
Normal file
86
src/server/api/routers/analytics.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { createTRPCRouter, adminProcedure } from "../utils";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
queryAnalytics,
|
||||
getAnalyticsSummary,
|
||||
getPathAnalytics,
|
||||
cleanupOldAnalytics
|
||||
} from "~/server/analytics";
|
||||
|
||||
export const analyticsRouter = createTRPCRouter({
|
||||
getLogs: adminProcedure
|
||||
.input(
|
||||
z.object({
|
||||
userId: z.string().optional(),
|
||||
path: z.string().optional(),
|
||||
startDate: z.string().optional(),
|
||||
endDate: z.string().optional(),
|
||||
limit: z.number().min(1).max(1000).default(100),
|
||||
offset: z.number().min(0).default(0)
|
||||
})
|
||||
)
|
||||
.query(async ({ input }) => {
|
||||
const logs = await queryAnalytics({
|
||||
userId: input.userId,
|
||||
path: input.path,
|
||||
startDate: input.startDate,
|
||||
endDate: input.endDate,
|
||||
limit: input.limit,
|
||||
offset: input.offset
|
||||
});
|
||||
|
||||
return {
|
||||
logs,
|
||||
count: logs.length,
|
||||
offset: input.offset,
|
||||
limit: input.limit
|
||||
};
|
||||
}),
|
||||
|
||||
getSummary: adminProcedure
|
||||
.input(
|
||||
z.object({
|
||||
days: z.number().min(1).max(365).default(30)
|
||||
})
|
||||
)
|
||||
.query(async ({ input }) => {
|
||||
const summary = await getAnalyticsSummary(input.days);
|
||||
|
||||
return {
|
||||
...summary,
|
||||
timeWindow: `${input.days} days`
|
||||
};
|
||||
}),
|
||||
|
||||
getPathStats: adminProcedure
|
||||
.input(
|
||||
z.object({
|
||||
path: z.string(),
|
||||
days: z.number().min(1).max(365).default(30)
|
||||
})
|
||||
)
|
||||
.query(async ({ input }) => {
|
||||
const stats = await getPathAnalytics(input.path, input.days);
|
||||
|
||||
return {
|
||||
path: input.path,
|
||||
...stats,
|
||||
timeWindow: `${input.days} days`
|
||||
};
|
||||
}),
|
||||
|
||||
cleanup: adminProcedure
|
||||
.input(
|
||||
z.object({
|
||||
olderThanDays: z.number().min(1).max(365).default(90)
|
||||
})
|
||||
)
|
||||
.mutation(async ({ input }) => {
|
||||
const deleted = await cleanupOldAnalytics(input.olderThanDays);
|
||||
|
||||
return {
|
||||
deleted,
|
||||
olderThanDays: input.olderThanDays
|
||||
};
|
||||
})
|
||||
});
|
||||
@@ -3,6 +3,8 @@ import type { APIEvent } from "@solidjs/start/server";
|
||||
import { getCookie, setCookie } from "vinxi/http";
|
||||
import { jwtVerify, type JWTPayload } from "jose";
|
||||
import { env } from "~/env/server";
|
||||
import { logVisit, enrichAnalyticsEntry } from "~/server/analytics";
|
||||
import { getRequestIP } from "vinxi/http";
|
||||
|
||||
export type Context = {
|
||||
event: APIEvent;
|
||||
@@ -33,6 +35,33 @@ async function createContextInner(event: APIEvent): Promise<Context> {
|
||||
}
|
||||
}
|
||||
|
||||
const req = event.nativeEvent.node?.req || event.nativeEvent;
|
||||
const path = req.url || event.request?.url || "unknown";
|
||||
const method = req.method || event.request?.method || "GET";
|
||||
const userAgent =
|
||||
req.headers?.["user-agent"] ||
|
||||
event.request?.headers?.get("user-agent") ||
|
||||
undefined;
|
||||
const referrer =
|
||||
req.headers?.referer ||
|
||||
req.headers?.referrer ||
|
||||
event.request?.headers?.get("referer") ||
|
||||
undefined;
|
||||
const ipAddress = getRequestIP(event.nativeEvent) || undefined;
|
||||
const sessionId = getCookie(event.nativeEvent, "session_id") || undefined;
|
||||
|
||||
logVisit(
|
||||
enrichAnalyticsEntry({
|
||||
userId,
|
||||
path,
|
||||
method,
|
||||
userAgent,
|
||||
referrer,
|
||||
ipAddress,
|
||||
sessionId
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
event,
|
||||
userId,
|
||||
|
||||
Reference in New Issue
Block a user