Files
flxn-app/src/features/players/server.ts
yohlo d2e1e5d4f0
Some checks failed
CI/CD Pipeline / Build and Push PocketBase Docker Image (push) Has been cancelled
CI/CD Pipeline / Deploy to Kubernetes (push) Has been cancelled
CI/CD Pipeline / Build and Push App Docker Image (push) Has been cancelled
more auth ree
2026-03-02 22:38:09 -06:00

207 lines
6.9 KiB
TypeScript

import { setUserMetadata, superTokensFunctionMiddleware, getSessionContext } from "@/utils/supertokens";
import { createServerFn } from "@tanstack/react-start";
import { Player, playerInputSchema, playerUpdateSchema, PlayerStats } from "@/features/players/types";
import { Match } from "@/features/matches/types";
import { pbAdmin } from "@/lib/pocketbase/client";
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 () =>
toServerResult(async () => {
const request = getRequest();
try {
const context = await getSessionContext(request, { isServerFunction: true });
await pbAdmin.authPromise;
const result = await pbAdmin.getPlayerByAuthId(context.userAuthId);
return {
user: result || undefined,
roles: context.roles,
metadata: context.metadata,
phone: context.phone
};
} catch (error: any) {
logger.info("FetchMe: Error caught", {
message: error?.message,
isResponse: error instanceof Response,
status: error instanceof Response ? error.status : error?.response?.status,
hasOptions: !!error?.options,
redirectTo: error?.options?.to
});
if (error?.options?.to && error?.options?.statusCode) {
logger.info("FetchMe: Redirect detected, re-throwing", error.options);
throw error;
}
if (error instanceof Response) {
const status = error.status;
if (status === 440) {
logger.info("FetchMe: Session refresh required (440)");
throw error;
}
}
if (error?.response?.status === 440 || error?.response?.status === 401) {
const errorData = error?.response?.data;
if (errorData?.error === "SESSION_REFRESH_REQUIRED") {
logger.info("FetchMe: Session refresh required (legacy)");
throw error;
}
}
if (error?.message === "SESSION_REFRESH_REQUIRED") {
logger.info("FetchMe: Session refresh required - returning empty auth (client will handle cleanup)");
return { user: undefined, roles: [], metadata: {}, phone: undefined };
}
if (error?.message === "Unauthenticated") {
logger.info("FetchMe: No authenticated user (expected when not logged in)");
return { user: undefined, roles: [], metadata: {}, phone: undefined };
}
logger.warn("FetchMe: Unexpected error, returning default", error);
return { user: undefined, roles: [], metadata: {}, phone: undefined };
}
})
);
export const getPlayer = createServerFn()
.inputValidator(z.string())
.middleware([superTokensFunctionMiddleware])
.handler(async ({ data }) =>
toServerResult<Player>(async () => await pbAdmin.getPlayer(data))
);
export const updatePlayer = createServerFn()
.inputValidator(playerUpdateSchema)
.middleware([superTokensFunctionMiddleware, serverFnLoggingMiddleware])
.handler(async ({ context, data }) =>
toServerResult(async () => {
const userAuthId = context.userAuthId;
if (!userAuthId) return;
const existing = await pbAdmin.getPlayerByAuthId(userAuthId);
if (!existing) return;
const updatedPlayer = await pbAdmin.updatePlayer(
existing.id!,
{
first_name: data.first_name,
last_name: data.last_name
}
);
logger.info('Updated player name', updatedPlayer);
await setUserMetadata({ data: { first_name: data.first_name, last_name: data.last_name } });
return updatedPlayer;
})
);
export const createPlayer = createServerFn()
.inputValidator(playerInputSchema)
.middleware([superTokensFunctionMiddleware])
.handler(async ({ context, data }) =>
toServerResult(async () => {
const userAuthId = context.userAuthId;
if (!userAuthId) return;
const existing = await pbAdmin.getPlayerByAuthId(userAuthId);
if (existing) return;
logger.info('Creating player', data, userAuthId);
const newPlayer = await pbAdmin.createPlayer({
auth_id: userAuthId,
first_name: data.first_name,
last_name: data.last_name
});
await setUserMetadata({ data: { first_name: data.first_name, last_name: data.last_name, player_id: newPlayer?.id?.toString() } });
logger.info('Created player', newPlayer);
return newPlayer;
})
);
export const associatePlayer = createServerFn()
.inputValidator(z.string())
.middleware([superTokensFunctionMiddleware, serverFnLoggingMiddleware])
.handler(async ({ context, data }) =>
toServerResult(async () => {
const userAuthId = context.userAuthId;
if (!userAuthId) return;
const p = await pbAdmin.getPlayer(data);
await pbAdmin.updatePlayer(data, {
auth_id: userAuthId
});
await setUserMetadata({ data: {
player_id: data,
first_name: p?.first_name,
last_name: p?.last_name
}});
const player = await pbAdmin.getPlayer(data);
logger.info('Associated player', player);
return player;
})
);
export const listPlayers = createServerFn()
.middleware([superTokensFunctionMiddleware])
.handler(async () =>
toServerResult(pbAdmin.listPlayers)
);
export const getUnassociatedPlayers = createServerFn()
.middleware([superTokensFunctionMiddleware])
.handler(async () =>
toServerResult(pbAdmin.getUnassociatedPlayers)
);
export const getPlayerStats = createServerFn()
.inputValidator(z.object({
playerId: z.string(),
viewType: z.enum(['all', 'mainline', 'regional']).optional()
}))
.middleware([superTokensFunctionMiddleware])
.handler(async ({ data }) =>
toServerResult<PlayerStats>(async () => await pbAdmin.getPlayerStats(data.playerId, data.viewType))
);
export const getAllPlayerStats = createServerFn()
.inputValidator(z.enum(['all', 'mainline', 'regional']).optional())
.middleware([superTokensFunctionMiddleware])
.handler(async ({ data }) =>
toServerResult<PlayerStats[]>(async () => await pbAdmin.getAllPlayerStats(data))
);
export const getPlayerMatches = createServerFn()
.inputValidator(z.string())
.middleware([superTokensFunctionMiddleware])
.handler(async ({ data }) =>
toServerResult<Match[]>(async () => await pbAdmin.getPlayerMatches(data))
);
export const getUnenrolledPlayers = createServerFn()
.inputValidator(z.string())
.middleware([superTokensFunctionMiddleware])
.handler(async ({ data: tournamentId }) =>
toServerResult(async () => await pbAdmin.getUnenrolledPlayers(tournamentId))
);
export const getPlayersActivity = createServerFn()
.middleware([superTokensFunctionMiddleware])
.handler(async () =>
toServerResult<Player[]>(async () => await pbAdmin.getPlayersActivity())
);