With the MCP plugin enabled, payload.auth() can return a payload-mcp-api-keys doc instead of a User. Add an isPayloadUser type guard and use it in every server action, page, and route that treats the auth result as a real user, so API-key sessions can no longer pass user auth checks. hasRoles now explicitly requires a users-collection doc.
91 lines
No EOL
2.9 KiB
TypeScript
91 lines
No EOL
2.9 KiB
TypeScript
import config from "@payload-config";
|
|
import { isPayloadUser } from "@/utils/access-control/isPayloadUser";
|
|
import { getPayload } from "payload";
|
|
import { headers as nextHeaders } from "next/headers";
|
|
import type { BankAccount, BankTransaction, Faction, Resource } from "@/payload-types";
|
|
import { LandmarkIcon } from "lucide-react";
|
|
import { BankingOverview } from "@/components/frontend/banking/BankingOverview";
|
|
import { hasPermission } from "@/utils/access-control/hasPermission";
|
|
import { hasLogisticsQualification } from "@/utils/access-control/hasLogisticsQualification";
|
|
|
|
export const metadata = {
|
|
title: "Banking — Polaris Task Force",
|
|
};
|
|
|
|
export default async function BankingPage() {
|
|
const payload = await getPayload({ config });
|
|
const { user: authUser } = await payload.auth({
|
|
headers: await nextHeaders(),
|
|
canSetHeaders: false,
|
|
});
|
|
const user = isPayloadUser(authUser) ? authUser : null;
|
|
|
|
const accountsRes = await payload.find({
|
|
collection: "bank-accounts",
|
|
sort: "name",
|
|
limit: 500,
|
|
depth: 1,
|
|
});
|
|
const accounts = accountsRes.docs as unknown as BankAccount[];
|
|
|
|
const transactionsRes = await payload.find({
|
|
collection: "bank-transactions",
|
|
sort: "-timestamp",
|
|
limit: 25,
|
|
depth: 1,
|
|
});
|
|
const recentTransactions = transactionsRes.docs as unknown as BankTransaction[];
|
|
|
|
const factionsRes = await payload.find({
|
|
collection: "factions",
|
|
limit: 100,
|
|
depth: 0,
|
|
});
|
|
const factions = (factionsRes.docs as unknown as Faction[]).map((f) => ({
|
|
id: f.id,
|
|
name: f.name,
|
|
}));
|
|
|
|
const isManager = user
|
|
? (await hasPermission(payload, user, "banking:manage")) ||
|
|
(await hasLogisticsQualification(payload, user).catch(() => false))
|
|
: false;
|
|
|
|
const currentUser = user
|
|
? {
|
|
id: user.id as number,
|
|
username: user.username,
|
|
displayName: user.displayName ?? user.username,
|
|
}
|
|
: null;
|
|
|
|
// Currency is shared across all accounts (main currency set in Game Rules).
|
|
// Resolve from the first account whose currency populated at depth 1.
|
|
const currencyResource =
|
|
accounts.length > 0 && typeof accounts[0].currency === "object"
|
|
? (accounts[0].currency as Resource)
|
|
: null;
|
|
|
|
return (
|
|
<div className="p-5 flex flex-col gap-6">
|
|
<div className="flex flex-col gap-1">
|
|
<div className="flex items-center gap-2">
|
|
<LandmarkIcon className="size-5 text-muted-foreground" />
|
|
<h1 className="text-lg font-semibold">Banking</h1>
|
|
</div>
|
|
<p className="text-sm text-muted-foreground">
|
|
Unit accounts, faction reserves, and personal wallets.
|
|
</p>
|
|
</div>
|
|
|
|
<BankingOverview
|
|
accounts={accounts}
|
|
recentTransactions={recentTransactions}
|
|
currentUser={currentUser}
|
|
currencyResource={currencyResource}
|
|
isManager={isManager}
|
|
factions={factions}
|
|
/>
|
|
</div>
|
|
);
|
|
} |