activity logging middleware
This commit is contained in:
@@ -9,6 +9,7 @@ import { MatchInput } from "@/features/matches/types";
|
||||
import { serverEvents } from "@/lib/events/emitter";
|
||||
import { superTokensFunctionMiddleware } from "@/utils/supertokens";
|
||||
import { PlayerInfo } from "../players/types";
|
||||
import { serverFnLoggingMiddleware } from "@/utils/activities";
|
||||
|
||||
const orderedTeamsSchema = z.object({
|
||||
tournamentId: z.string(),
|
||||
@@ -17,7 +18,7 @@ const orderedTeamsSchema = z.object({
|
||||
|
||||
export const generateTournamentBracket = createServerFn()
|
||||
.inputValidator(orderedTeamsSchema)
|
||||
.middleware([superTokensAdminFunctionMiddleware])
|
||||
.middleware([superTokensAdminFunctionMiddleware, serverFnLoggingMiddleware])
|
||||
.handler(async ({ data: { tournamentId, orderedTeamIds } }) =>
|
||||
toServerResult(async () => {
|
||||
logger.info("Generating tournament bracket", {
|
||||
@@ -138,7 +139,7 @@ export const generateTournamentBracket = createServerFn()
|
||||
|
||||
export const startMatch = createServerFn()
|
||||
.inputValidator(z.string())
|
||||
.middleware([superTokensAdminFunctionMiddleware])
|
||||
.middleware([superTokensAdminFunctionMiddleware, serverFnLoggingMiddleware])
|
||||
.handler(async ({ data }) =>
|
||||
toServerResult(async () => {
|
||||
logger.info("Starting match", data);
|
||||
@@ -171,7 +172,7 @@ const endMatchSchema = z.object({
|
||||
});
|
||||
export const endMatch = createServerFn()
|
||||
.inputValidator(endMatchSchema)
|
||||
.middleware([superTokensAdminFunctionMiddleware])
|
||||
.middleware([superTokensAdminFunctionMiddleware, serverFnLoggingMiddleware])
|
||||
.handler(async ({ data: { matchId, home_cups, away_cups, ot_count } }) =>
|
||||
toServerResult(async () => {
|
||||
logger.info("Ending match", matchId);
|
||||
|
||||
@@ -7,6 +7,7 @@ import { z } from "zod";
|
||||
import { logger } from ".";
|
||||
import { getRequest } from "@tanstack/react-start/server";
|
||||
import { toServerResult } from "@/lib/tanstack-query/utils/to-server-result";
|
||||
import { serverFnLoggingMiddleware } from "@/utils/activities";
|
||||
|
||||
export const fetchMe = createServerFn()
|
||||
.handler(async () =>
|
||||
@@ -46,7 +47,7 @@ export const getPlayer = createServerFn()
|
||||
|
||||
export const updatePlayer = createServerFn()
|
||||
.inputValidator(playerUpdateSchema)
|
||||
.middleware([superTokensFunctionMiddleware])
|
||||
.middleware([superTokensFunctionMiddleware, serverFnLoggingMiddleware])
|
||||
.handler(async ({ context, data }) =>
|
||||
toServerResult(async () => {
|
||||
const userAuthId = context.userAuthId;
|
||||
@@ -98,7 +99,7 @@ export const createPlayer = createServerFn()
|
||||
|
||||
export const associatePlayer = createServerFn()
|
||||
.inputValidator(z.string())
|
||||
.middleware([superTokensFunctionMiddleware])
|
||||
.middleware([superTokensFunctionMiddleware, serverFnLoggingMiddleware])
|
||||
.handler(async ({ context, data }) =>
|
||||
toServerResult(async () => {
|
||||
const userAuthId = context.userAuthId;
|
||||
|
||||
@@ -6,6 +6,7 @@ import { toServerResult } from "@/lib/tanstack-query/utils/to-server-result";
|
||||
import { teamInputSchema, teamUpdateSchema } from "./types";
|
||||
import { logger } from "@/lib/logger";
|
||||
import { Match } from "../matches/types";
|
||||
import { serverFnLoggingMiddleware } from "@/utils/activities";
|
||||
|
||||
|
||||
export const listTeamInfos = createServerFn()
|
||||
@@ -30,7 +31,7 @@ export const getTeamInfo = createServerFn()
|
||||
|
||||
export const createTeam = createServerFn()
|
||||
.inputValidator(teamInputSchema)
|
||||
.middleware([superTokensFunctionMiddleware])
|
||||
.middleware([superTokensFunctionMiddleware, serverFnLoggingMiddleware])
|
||||
.handler(async ({ data, context }) =>
|
||||
toServerResult(async () => {
|
||||
const userId = context.userAuthId;
|
||||
@@ -50,7 +51,7 @@ export const updateTeam = createServerFn()
|
||||
id: z.string(),
|
||||
updates: teamUpdateSchema
|
||||
}))
|
||||
.middleware([superTokensFunctionMiddleware])
|
||||
.middleware([superTokensFunctionMiddleware, serverFnLoggingMiddleware])
|
||||
.handler(async ({ data: { id, updates }, context }) =>
|
||||
toServerResult(async () => {
|
||||
const userId = context.userAuthId;
|
||||
|
||||
@@ -5,6 +5,7 @@ import { tournamentInputSchema } from "@/features/tournaments/types";
|
||||
import { logger } from ".";
|
||||
import { z } from "zod";
|
||||
import { toServerResult } from "@/lib/tanstack-query/utils/to-server-result";
|
||||
import { serverFnLoggingMiddleware } from "@/utils/activities";
|
||||
|
||||
export const listTournaments = createServerFn()
|
||||
.middleware([superTokensFunctionMiddleware])
|
||||
@@ -14,7 +15,7 @@ export const listTournaments = createServerFn()
|
||||
|
||||
export const createTournament = createServerFn()
|
||||
.inputValidator(tournamentInputSchema)
|
||||
.middleware([superTokensAdminFunctionMiddleware])
|
||||
.middleware([superTokensAdminFunctionMiddleware, serverFnLoggingMiddleware])
|
||||
.handler(async ({ data }) =>
|
||||
toServerResult(() => pbAdmin.createTournament(data))
|
||||
);
|
||||
@@ -24,7 +25,7 @@ export const updateTournament = createServerFn()
|
||||
id: z.string(),
|
||||
updates: tournamentInputSchema.partial()
|
||||
}))
|
||||
.middleware([superTokensAdminFunctionMiddleware])
|
||||
.middleware([superTokensAdminFunctionMiddleware, serverFnLoggingMiddleware])
|
||||
.handler(async ({ data }) =>
|
||||
toServerResult(() => pbAdmin.updateTournament(data.id, data.updates))
|
||||
);
|
||||
@@ -48,7 +49,7 @@ export const enrollTeam = createServerFn()
|
||||
tournamentId: z.string(),
|
||||
teamId: z.string()
|
||||
}))
|
||||
.middleware([superTokensFunctionMiddleware])
|
||||
.middleware([superTokensFunctionMiddleware, serverFnLoggingMiddleware])
|
||||
.handler(async ({ data: { tournamentId, teamId }, context }) =>
|
||||
toServerResult(async () => {
|
||||
const userId = context.userAuthId;
|
||||
@@ -74,7 +75,7 @@ export const unenrollTeam = createServerFn()
|
||||
tournamentId: z.string(),
|
||||
teamId: z.string()
|
||||
}))
|
||||
.middleware([superTokensFunctionMiddleware])
|
||||
.middleware([superTokensFunctionMiddleware, serverFnLoggingMiddleware])
|
||||
.handler(async ({ data: { tournamentId, teamId }, context }) =>
|
||||
toServerResult(() => pbAdmin.unenrollTeam(tournamentId, teamId))
|
||||
);
|
||||
@@ -95,7 +96,7 @@ export const getFreeAgents = createServerFn()
|
||||
|
||||
export const enrollFreeAgent = createServerFn()
|
||||
.inputValidator(z.object({ phone: z.string(), tournamentId: z.string() }))
|
||||
.middleware([superTokensFunctionMiddleware])
|
||||
.middleware([superTokensFunctionMiddleware, serverFnLoggingMiddleware])
|
||||
.handler(async ({ context, data }) =>
|
||||
toServerResult(async () => {
|
||||
const userAuthId = context.userAuthId;
|
||||
@@ -109,7 +110,7 @@ export const enrollFreeAgent = createServerFn()
|
||||
|
||||
export const unenrollFreeAgent = createServerFn()
|
||||
.inputValidator(z.object({ tournamentId: z.string() }))
|
||||
.middleware([superTokensFunctionMiddleware])
|
||||
.middleware([superTokensFunctionMiddleware, serverFnLoggingMiddleware])
|
||||
.handler(async ({ context, data }) =>
|
||||
toServerResult(async () => {
|
||||
const userAuthId = context.userAuthId;
|
||||
|
||||
@@ -4,6 +4,7 @@ import { createTournamentsService } from "./services/tournaments";
|
||||
import { createTeamsService } from "./services/teams";
|
||||
import { createMatchesService } from "./services/matches";
|
||||
import { createReactionsService } from "./services/reactions";
|
||||
import { createActivitiesService } from "./services/activities";
|
||||
import dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
|
||||
@@ -35,6 +36,7 @@ class PocketBaseAdminClient {
|
||||
Object.assign(this, createTournamentsService(this.pb));
|
||||
Object.assign(this, createMatchesService(this.pb));
|
||||
Object.assign(this, createReactionsService(this.pb));
|
||||
Object.assign(this, createActivitiesService(this.pb));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -54,7 +56,8 @@ interface AdminClient
|
||||
ReturnType<typeof createTeamsService>,
|
||||
ReturnType<typeof createTournamentsService>,
|
||||
ReturnType<typeof createMatchesService>,
|
||||
ReturnType<typeof createReactionsService> {
|
||||
ReturnType<typeof createReactionsService>,
|
||||
ReturnType<typeof createActivitiesService> {
|
||||
authPromise: Promise<void>;
|
||||
}
|
||||
|
||||
|
||||
56
src/lib/pocketbase/services/activities.ts
Normal file
56
src/lib/pocketbase/services/activities.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import PocketBase from "pocketbase";
|
||||
|
||||
export interface Activity {
|
||||
id: string;
|
||||
name: string;
|
||||
player?: string;
|
||||
duration: number;
|
||||
success: boolean;
|
||||
error?: string;
|
||||
arguments?: any;
|
||||
user_agent?: string;
|
||||
created: string;
|
||||
updated: string;
|
||||
}
|
||||
|
||||
export interface ActivityInput {
|
||||
name: string;
|
||||
player?: string;
|
||||
duration: number;
|
||||
success: boolean;
|
||||
error?: string;
|
||||
arguments?: any;
|
||||
user_agent?: string;
|
||||
}
|
||||
|
||||
export function createActivitiesService(pb: PocketBase) {
|
||||
return {
|
||||
async createActivity(data: ActivityInput): Promise<Activity> {
|
||||
const result = await pb.collection("activities").create<Activity>(data);
|
||||
return result;
|
||||
},
|
||||
|
||||
async getRecentActivities(limit: number = 100): Promise<Activity[]> {
|
||||
const result = await pb.collection("activities").getList<Activity>(1, limit, {
|
||||
sort: "-created",
|
||||
});
|
||||
return result.items;
|
||||
},
|
||||
|
||||
async getActivitiesByUser(userId: string, limit: number = 50): Promise<Activity[]> {
|
||||
const result = await pb.collection("activities").getList<Activity>(1, limit, {
|
||||
filter: `user_id = "${userId}"`,
|
||||
sort: "-created",
|
||||
});
|
||||
return result.items;
|
||||
},
|
||||
|
||||
async getActivitiesByFunction(functionName: string, limit: number = 50): Promise<Activity[]> {
|
||||
const result = await pb.collection("activities").getList<Activity>(1, limit, {
|
||||
filter: `function_name = "${functionName}"`,
|
||||
sort: "-created",
|
||||
});
|
||||
return result.items;
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -17,8 +17,8 @@ export const backendConfig = (): TypeInput => {
|
||||
},
|
||||
appInfo,
|
||||
recipeList: [
|
||||
PasswordlessTwilioVerify.init(),
|
||||
//PasswordlessDevelopmentMode.init(),
|
||||
//PasswordlessTwilioVerify.init(),
|
||||
PasswordlessDevelopmentMode.init(),
|
||||
Session.init({
|
||||
cookieSameSite: "lax",
|
||||
cookieSecure: import.meta.env.NODE_ENV === "production",
|
||||
|
||||
54
src/utils/activities.ts
Normal file
54
src/utils/activities.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { pbAdmin } from "@/lib/pocketbase/client";
|
||||
import { createMiddleware } from "@tanstack/react-start";
|
||||
import { getRequest } from "@tanstack/react-start/server";
|
||||
|
||||
export const serverFnLoggingMiddleware = createMiddleware({
|
||||
type: "function",
|
||||
}).server(async ({ next, data, functionId, context }) => {
|
||||
const request = getRequest();
|
||||
|
||||
const serverFnName = functionId.split('--')[1]?.split('_')[0] || 'unknown';
|
||||
const userId = (context as any)?.metadata?.player_id || 'unknown';
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
const result = await next();
|
||||
const duration = Date.now() - startTime;
|
||||
|
||||
|
||||
try {
|
||||
await pbAdmin.authPromise;
|
||||
await pbAdmin.createActivity({
|
||||
name: serverFnName,
|
||||
player: userId !== 'unknown' ? userId : undefined,
|
||||
duration,
|
||||
success: true,
|
||||
arguments: data,
|
||||
user_agent: request.headers.get('user-agent') || undefined,
|
||||
});
|
||||
} catch (activityError) {
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
const duration = Date.now() - startTime;
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
|
||||
try {
|
||||
await pbAdmin.authPromise;
|
||||
await pbAdmin.createActivity({
|
||||
name: serverFnName,
|
||||
player: userId !== 'unknown' ? userId : undefined,
|
||||
duration,
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
arguments: data,
|
||||
user_agent: request.headers.get('user-agent') || undefined,
|
||||
});
|
||||
} catch (activityError) {
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user