1
0
Fork 0
polaris-task-force/src/collections/intelligence/Factions.ts
Z8MB1E 653caa8064 feat(rbac): migrate collection access controls to RBAC permissions
Replace legacy isDeveloper/isAdmin/hasRoles checks with
hasPermission/requirePermission across all collections. Add roleDocs
relationship to Users, enlistmentDate to Profiles, and field-level
intel_excerpt permission gate on Profiles. Auto-generated payload-types
updated to reflect the new Roles collection and roleDocs field.
2026-08-19 19:47:51 -04:00

167 lines
4.3 KiB
TypeScript

import { CollectionConfig } from "payload";
import { requirePermission } from "@/utils/access-control/hasPermission";
export const Factions: CollectionConfig = {
slug: "factions",
admin: {
group: "Intelligence",
useAsTitle: "name",
},
access: {
create: requirePermission("factions:create"),
update: requirePermission("factions:update"),
delete: requirePermission("factions:delete"),
read: ({ req }) => !!req.user,
},
hooks: {
beforeValidate: [
async ({ data, operation, originalDoc, req }) => {
if (data?.type !== "playerFaction") return data;
const payload = req.payload;
const existing = await payload.find({
collection: "factions",
where: { type: { equals: "playerFaction" } },
limit: 1,
depth: 0,
overrideAccess: true,
});
const conflict = existing.docs.find((doc) => doc.id !== originalDoc?.id);
if (conflict) {
throw new Error(
`Only one faction can be the Player Faction at a time. "${conflict.name}" is already the Player Faction.`,
);
}
return data;
},
],
},
fields: [
{
name: "name",
type: "text",
required: true,
unique: true,
},
{
name: "type",
type: "select",
options: [
{
label: "Player Faction",
value: "playerFaction",
},
{
label: "Major Faction",
value: "majorFaction",
},
{
label: "Minor Faction",
value: "minorFaction",
},
{
label: "Corporation",
value: "corporation",
},
{
label: "Special",
value: "special",
},
],
required: true,
defaultValue: "minorFaction",
admin: {
description:
"Player Faction = the faction our unit is currently playing as. Only one faction can carry this type at a time.",
},
},
{
name: "description",
type: "richText",
},
{
name: "reputation",
type: "number",
admin: {
description:
"This faction's standing in the world, from -100 (reviled) to 100 (revered). 0 is neutral. Adjusted manually by admins/GMs as campaign events unfold.",
},
defaultValue: 0,
min: -100,
max: 100,
},
{
name: "relationships",
type: "array",
labels: {
singular: "Relationship",
plural: "Relationships",
},
admin: {
description:
"Pairwise standing between this faction and another faction, from -100 (hostile) to 100 (allied). 0 is neutral. One entry per counterpart faction.",
},
fields: [
{
name: "targetFaction",
type: "relationship",
relationTo: "factions",
required: true,
admin: {
description: "The other faction in this pairwise relationship.",
},
},
{
name: "value",
type: "number",
required: true,
defaultValue: 0,
min: -100,
max: 100,
admin: {
description: "Standing from -100 (hostile) to 100 (allied). 0 is neutral.",
},
},
{
name: "notes",
type: "textarea",
admin: {
description: "Optional context for why this relationship stands where it does.",
},
},
],
},
{
name: "playableFaction",
type: "checkbox",
admin: {
description:
"Whether or not this faction is playable. Some factions may be specifically intended to be unplayable based on narrative requirements.",
},
},
{
name: "startingFaction",
type: "checkbox",
admin: {
description:
"Whether or not this faction is/was available as a starting faction at the beginning of the campaign.",
},
},
{
name: "associatedCampaigns",
type: "relationship",
relationTo: "campaigns",
hasMany: true,
},
{
name: "coverImage",
type: "upload",
relationTo: "media",
admin: {
description:
"Background image for the faction detail page hero. Renders translucent with a top-to-bottom fade to black. Recommended aspect ratio: 16:9 or wider.",
},
},
],
};