1
0
Fork 0
polaris-task-force/src/app/(frontend)/minigames/poker/actions.ts
Z8MB1E 34dcefec54 feat(poker): felt UI popups, named table display, and dev controls
Table components, popup system, dev panel, and the actions/page wiring, with popup, table, and dev-control tests.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-14 15:37:19 -04:00

238 lines
6.7 KiB
TypeScript

"use server";
/**
* Canonical server-action surface for the poker minigame.
* The signatures below are the pinned contract shared with the UI layer.
* All table logic lives in the registry (src/lib/minigames/poker/tables.ts).
*/
import config from "@payload-config";
import { getPayload } from "payload";
import type { Card, PokerActionKind, PrivatePlayerView } from "@/lib/minigames/poker/types";
import { isSuperuser } from "@/utils/access-control/hasPermission";
import {
adjustStack,
applyPlayerAction,
createTable,
forceExpireTable,
getSnapshotFor,
joinTableWithBuyIn,
leaveTableWithBuyIn,
spectateTable,
listSummaries,
renameTable,
setBoardQueue,
setHoleQueue,
startGameWithBuyIn,
type PokerSnapshot,
type PokerTableSummary,
} from "@/lib/minigames/poker/tables";
export type { PokerSnapshot, PokerTableSummary };
export type PokerCardInput = Card;
interface ActionResult<T = undefined> {
success: boolean;
error?: string;
data?: T;
}
async function authenticate() {
const payloadConfig = await config;
const payload = await getPayload({ config: payloadConfig });
const { headers } = await import("next/headers");
const hdrs = await headers();
const { user } = await payload.auth({
headers: hdrs,
canSetHeaders: false,
});
if (!user) {
throw new Error("Unauthorized");
}
return { payload, user };
}
function toError(error: unknown): { success: false; error: string } {
const message = error instanceof Error ? error.message : "Unknown error";
if (message === "Unauthorized") {
return { success: false, error: "You must be logged in." };
}
return { success: false, error: message };
}
interface UserLike {
id: number;
displayName?: string | null;
username?: string | null;
}
function userName(user: UserLike): string {
if (typeof user.displayName === "string" && user.displayName) return user.displayName;
if (typeof user.username === "string" && user.username) return user.username;
return `Player ${user.id}`;
}
export async function createPokerTable(): Promise<ActionResult<{ tableId: string }>> {
try {
const { user } = await authenticate();
const tableId = createTable(Number(user.id), userName(user));
return { success: true, data: { tableId } };
} catch (error) {
return toError(error);
}
}
export async function listPokerTables(): Promise<ActionResult<PokerTableSummary[]>> {
try {
await authenticate();
return { success: true, data: listSummaries() };
} catch (error) {
return toError(error);
}
}
export async function forceExpirePokerTable(tableId: string): Promise<ActionResult<undefined>> {
try {
const { payload, user } = await authenticate();
const allowed = await isSuperuser(payload, user);
if (!allowed) {
return { success: false, error: "Only administrators can force-end tables." };
}
forceExpireTable(tableId, payload);
return { success: true };
} catch (error) {
return toError(error);
}
}
export async function joinPokerTable(tableId: string): Promise<ActionResult<PokerSnapshot>> {
try {
const { user } = await authenticate();
const snapshot = await joinTableWithBuyIn(tableId, Number(user.id), userName(user));
return { success: true, data: snapshot };
} catch (error) {
return toError(error);
}
}
export async function spectatePokerTable(tableId: string): Promise<ActionResult<PokerSnapshot>> {
try {
const { user } = await authenticate();
const snapshot = spectateTable(tableId, Number(user.id));
return { success: true, data: snapshot };
} catch (error) {
return toError(error);
}
}
export async function setPokerBoardQueue(
tableId: string,
cards: PokerCardInput[],
): Promise<ActionResult<PokerSnapshot>> {
try {
const { payload, user } = await authenticate();
const allowed = await isSuperuser(payload, user);
if (!allowed) {
return { success: false, error: "Only administrators can use dev controls." };
}
const snapshot = setBoardQueue(tableId, Number(user.id), cards);
return { success: true, data: snapshot };
} catch (error) {
return toError(error);
}
}
export async function setPokerHoleQueue(
tableId: string,
seatId: number,
cards: PokerCardInput[],
): Promise<ActionResult<PokerSnapshot>> {
try {
const { payload, user } = await authenticate();
const allowed = await isSuperuser(payload, user);
if (!allowed) {
return { success: false, error: "Only administrators can use dev controls." };
}
const snapshot = setHoleQueue(tableId, Number(user.id), seatId, cards);
return { success: true, data: snapshot };
} catch (error) {
return toError(error);
}
}
export async function adjustPokerStack(
tableId: string,
seatId: number,
delta: number,
): Promise<ActionResult<PokerSnapshot>> {
try {
const { payload, user } = await authenticate();
const allowed = await isSuperuser(payload, user);
if (!allowed) {
return { success: false, error: "Only administrators can use dev controls." };
}
const snapshot = adjustStack(tableId, Number(user.id), seatId, delta);
return { success: true, data: snapshot };
} catch (error) {
return toError(error);
}
}
export async function leavePokerTable(tableId: string): Promise<ActionResult<undefined>> {
try {
const { user } = await authenticate();
await leaveTableWithBuyIn(tableId, Number(user.id));
return { success: true };
} catch (error) {
return toError(error);
}
}
export async function startPokerGame(tableId: string): Promise<ActionResult<PokerSnapshot>> {
try {
const { user } = await authenticate();
const snapshot = await startGameWithBuyIn(tableId, Number(user.id));
return { success: true, data: snapshot };
} catch (error) {
return toError(error);
}
}
export async function renamePokerTable(
tableId: string,
rawName: string,
): Promise<ActionResult<PokerSnapshot>> {
try {
const { user } = await authenticate();
const snapshot = renameTable(tableId, Number(user.id), rawName);
return { success: true, data: snapshot };
} catch (error) {
return toError(error);
}
}
export async function fetchPokerTable(tableId: string): Promise<ActionResult<PokerSnapshot>> {
try {
const { user } = await authenticate();
const snapshot = getSnapshotFor(tableId, Number(user.id));
return { success: true, data: snapshot };
} catch (error) {
return toError(error);
}
}
export async function pokerAction(
tableId: string,
kind: PokerActionKind,
amount?: number,
): Promise<ActionResult<PokerSnapshot>> {
try {
const { user } = await authenticate();
const snapshot = applyPlayerAction(tableId, Number(user.id), kind, amount);
return { success: true, data: snapshot };
} catch (error) {
return toError(error);
}
}