1
0
Fork 0

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.
This commit is contained in:
Jason Fraley 2026-08-19 19:47:51 -04:00
parent 80f28ed939
commit 653caa8064
35 changed files with 544 additions and 230 deletions

View file

@ -1,11 +1,11 @@
import { GlobalConfig } from "payload"; import { GlobalConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission } from "@/utils/access-control/hasPermission";
export const Shims: GlobalConfig = { export const Shims: GlobalConfig = {
slug: "shims", slug: "shims",
access: { access: {
update: isDeveloper, update: requirePermission("shims:update"),
read: isDeveloper, read: requirePermission("shims:read"),
}, },
fields: [ fields: [
{ {

View file

@ -1,6 +1,5 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission, hasPermission } from "@/utils/access-control/hasPermission";
import hasRoles from "@/utils/access-control/hasRoles";
export const BankAccounts: CollectionConfig = { export const BankAccounts: CollectionConfig = {
slug: "bank-accounts", slug: "bank-accounts",
@ -19,9 +18,15 @@ export const BankAccounts: CollectionConfig = {
}, },
access: { access: {
read: ({ req }) => !!req.user, read: ({ req }) => !!req.user,
create: ({ req }) => hasRoles(["admin", "developer"], req.user), create: async ({ req }) => {
update: ({ req }) => hasRoles(["admin", "developer"], req.user), return hasPermission(req.payload, req.user, "bank-accounts:create");
delete: isDeveloper, },
update: async ({ req }) => {
return hasPermission(req.payload, req.user, "bank-accounts:update");
},
delete: async ({ req }) => {
return hasPermission(req.payload, req.user, "bank-accounts:delete");
},
}, },
fields: [ fields: [
{ {

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission } from "@/utils/access-control/hasPermission";
export const BankTransactions: CollectionConfig = { export const BankTransactions: CollectionConfig = {
slug: "bank-transactions", slug: "bank-transactions",
@ -19,9 +19,9 @@ export const BankTransactions: CollectionConfig = {
}, },
access: { access: {
read: ({ req }) => !!req.user, read: ({ req }) => !!req.user,
create: isDeveloper, create: requirePermission("bank-transactions:create"),
update: isDeveloper, update: requirePermission("bank-transactions:update"),
delete: isDeveloper, delete: requirePermission("bank-transactions:delete"),
}, },
hooks: { hooks: {
beforeValidate: [ beforeValidate: [

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission } from "@/utils/access-control/hasPermission";
export const LedgerEntries: CollectionConfig = { export const LedgerEntries: CollectionConfig = {
slug: "ledger-entries", slug: "ledger-entries",
@ -11,9 +11,9 @@ export const LedgerEntries: CollectionConfig = {
}, },
access: { access: {
read: ({ req }) => !!req.user, read: ({ req }) => !!req.user,
create: isDeveloper, create: requirePermission("ledger-entries:create"),
update: isDeveloper, update: requirePermission("ledger-entries:update"),
delete: isDeveloper, delete: requirePermission("ledger-entries:delete"),
}, },
fields: [ fields: [
{ {

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission, hasPermission } from "@/utils/access-control/hasPermission";
const TARGET_COLLECTIONS = [ const TARGET_COLLECTIONS = [
{ label: "Game Structures", value: "game-structures" }, { label: "Game Structures", value: "game-structures" },
@ -38,12 +38,13 @@ export const GameEventLogs: CollectionConfig = {
access: { access: {
read: ({ req }) => !!req.user, read: ({ req }) => !!req.user,
create: ({ req }) => !!req.user, create: ({ req }) => !!req.user,
update: ({ req }) => { update: async ({ req }) => {
if (!req.user) return false; if (!req.user) return false;
const roles = req.user.roles as string[] | undefined; return await hasPermission(req.payload, req.user, "game-event-logs:update");
return roles?.includes("admin") || roles?.includes("developer") || false; },
delete: async ({ req }) => {
return await hasPermission(req.payload, req.user, "game-event-logs:delete");
}, },
delete: isDeveloper,
}, },
fields: [ fields: [
{ {

View file

@ -1,12 +1,12 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission } from "@/utils/access-control/hasPermission";
export const GameHardResources: CollectionConfig = { export const GameHardResources: CollectionConfig = {
slug: "game-hard-resources", slug: "game-hard-resources",
access: { access: {
create: isDeveloper, create: requirePermission("game-hard-resources:create"),
update: isDeveloper, update: requirePermission("game-hard-resources:update"),
delete: isDeveloper, delete: requirePermission("game-hard-resources:delete"),
}, },
admin: { admin: {
group: "Game", group: "Game",

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission } from "@/utils/access-control/hasPermission";
export const GameNpcs: CollectionConfig = { export const GameNpcs: CollectionConfig = {
slug: "game-npcs", slug: "game-npcs",
@ -12,9 +12,9 @@ export const GameNpcs: CollectionConfig = {
}, },
access: { access: {
read: ({ req }) => !!req.user, read: ({ req }) => !!req.user,
create: isDeveloper, create: requirePermission("game-npcs:create"),
update: isDeveloper, update: requirePermission("game-npcs:update"),
delete: isDeveloper, delete: requirePermission("game-npcs:delete"),
}, },
fields: [ fields: [
{ {

View file

@ -1,11 +1,11 @@
import { GlobalConfig } from "payload"; import { GlobalConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission } from "@/utils/access-control/hasPermission";
export const GameRules: GlobalConfig = { export const GameRules: GlobalConfig = {
slug: "game-rules", slug: "game-rules",
access: { access: {
read: isDeveloper, read: requirePermission("game-rules:read"),
update: isDeveloper, update: requirePermission("game-rules:update"),
}, },
fields: [ fields: [
{ {

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission } from "@/utils/access-control/hasPermission";
import { emitGameEvent } from "@/utils/event-log/emit"; import { emitGameEvent } from "@/utils/event-log/emit";
export const GameStructures: CollectionConfig = { export const GameStructures: CollectionConfig = {
@ -10,9 +10,9 @@ export const GameStructures: CollectionConfig = {
}, },
access: { access: {
read: ({ req }) => !!req.user, read: ({ req }) => !!req.user,
create: isDeveloper, create: requirePermission("game-structures:create"),
update: isDeveloper, update: requirePermission("game-structures:update"),
delete: isDeveloper, delete: requirePermission("game-structures:delete"),
}, },
hooks: { hooks: {
afterChange: [ afterChange: [

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission } from "@/utils/access-control/hasPermission";
export const GameVehicles: CollectionConfig = { export const GameVehicles: CollectionConfig = {
slug: "game-vehicles", slug: "game-vehicles",
@ -9,9 +9,9 @@ export const GameVehicles: CollectionConfig = {
}, },
access: { access: {
read: ({ req }) => !!req.user, read: ({ req }) => !!req.user,
create: isDeveloper, create: requirePermission("game-vehicles:create"),
update: isDeveloper, update: requirePermission("game-vehicles:update"),
delete: isDeveloper, delete: requirePermission("game-vehicles:delete"),
}, },
fields: [ fields: [
{ {

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission } from "@/utils/access-control/hasPermission";
export const Campaigns: CollectionConfig = { export const Campaigns: CollectionConfig = {
slug: "campaigns", slug: "campaigns",
@ -8,9 +8,9 @@ export const Campaigns: CollectionConfig = {
useAsTitle: "name", useAsTitle: "name",
}, },
access: { access: {
create: isDeveloper, create: requirePermission("campaigns:create"),
update: isDeveloper, update: requirePermission("campaigns:update"),
delete: isDeveloper, delete: requirePermission("campaigns:delete"),
read: ({ req }) => !!req.user, read: ({ req }) => !!req.user,
}, },
fields: [ fields: [

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission } from "@/utils/access-control/hasPermission";
export const Factions: CollectionConfig = { export const Factions: CollectionConfig = {
slug: "factions", slug: "factions",
@ -8,9 +8,9 @@ export const Factions: CollectionConfig = {
useAsTitle: "name", useAsTitle: "name",
}, },
access: { access: {
create: isDeveloper, create: requirePermission("factions:create"),
update: isDeveloper, update: requirePermission("factions:update"),
delete: isDeveloper, delete: requirePermission("factions:delete"),
read: ({ req }) => !!req.user, read: ({ req }) => !!req.user,
}, },
hooks: { hooks: {

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import hasRoles from "@/utils/access-control/hasRoles"; import { requirePermission, hasPermission } from "@/utils/access-control/hasPermission";
export const MissionAttendances: CollectionConfig = { export const MissionAttendances: CollectionConfig = {
slug: "mission-attendances", slug: "mission-attendances",
@ -8,16 +8,14 @@ export const MissionAttendances: CollectionConfig = {
defaultColumns: ["mission", "user", "response"], defaultColumns: ["mission", "user", "response"],
}, },
access: { access: {
read: ({ req }) => !!req.user, read: async ({ req }) => !!req.user,
create: ({ req, data }) => { create: async ({ req, data }) => {
if (hasRoles(["admin", "developer"], req.user)) return true;
if (!req.user || !data) return false; if (!req.user || !data) return false;
const ownerId = typeof data.user === "object" ? data.user?.id : data.user; return await hasPermission(req.payload, req.user, "mission-attendances:create");
return ownerId === req.user.id;
}, },
update: async ({ req, data, id }) => { update: async ({ req, data, id }) => {
if (hasRoles(["admin", "developer"], req.user)) return true;
if (!req.user || typeof id !== "number") return false; if (!req.user || typeof id !== "number") return false;
if (await hasPermission(req.payload, req.user, "mission-attendances:update")) return true;
const ownerId = typeof data?.user === "object" ? data.user?.id : data?.user; const ownerId = typeof data?.user === "object" ? data.user?.id : data?.user;
if (ownerId === req.user.id) return true; if (ownerId === req.user.id) return true;
@ -34,7 +32,9 @@ export const MissionAttendances: CollectionConfig = {
const docOwner = typeof doc.user === "object" ? doc.user?.id : doc.user; const docOwner = typeof doc.user === "object" ? doc.user?.id : doc.user;
return docOwner === req.user.id; return docOwner === req.user.id;
}, },
delete: ({ req }) => hasRoles(["admin", "developer"], req.user), delete: async ({ req }) => {
return await hasPermission(req.payload, req.user, "mission-attendances:delete");
},
}, },
fields: [ fields: [
{ {

View file

@ -1,6 +1,5 @@
import type { CollectionConfig, Payload } from "payload"; import type { CollectionConfig, Payload } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission, hasPermission } from "@/utils/access-control/hasPermission";
import hasRoles from "@/utils/access-control/hasRoles";
type AssignmentRef = { id: number } | number; type AssignmentRef = { id: number } | number;
@ -40,12 +39,18 @@ export const Missions: CollectionConfig = {
useAsTitle: "name", useAsTitle: "name",
}, },
access: { access: {
create: isDeveloper, create: async ({ req }) => {
update: isDeveloper, return await hasPermission(req.payload, req.user, "missions:create");
delete: isDeveloper, },
update: async ({ req }) => {
return await hasPermission(req.payload, req.user, "missions:update");
},
delete: async ({ req }) => {
return await hasPermission(req.payload, req.user, "missions:delete");
},
read: async ({ req, data }) => { read: async ({ req, data }) => {
if (!req.user) return false; if (!req.user) return false;
if (hasRoles(["developer", "admin"], req.user)) return true; if (await hasPermission(req.payload, req.user, "missions:read")) return true;
const userId = req.user.id; const userId = req.user.id;
@ -126,27 +131,23 @@ export const Missions: CollectionConfig = {
value: "external", value: "external",
}, },
], ],
filterOptions: ({ req }) => { filterOptions: ({ req }) => {
return [ return [
{ {
label: "Side Operation", label: "Side Operation",
value: "side", value: "side",
}, },
{ {
label: "External Operation", label: "External Operation",
value: "external", value: "external",
}, },
...(isDeveloper({ req: req }) {
? [ label: "Main Operation",
{ value: "main",
label: "Main Operation", },
value: "main", ];
}, },
] defaultValue: ({ req }) => "side",
: []),
];
},
defaultValue: ({ req }) => (isDeveloper({ req: req }) ? "main" : "side"),
required: true, required: true,
}, },
{ {
@ -504,9 +505,11 @@ export const Missions: CollectionConfig = {
type: "text", type: "text",
hidden: true, hidden: true,
defaultValue: "ptf313", defaultValue: "ptf313",
access: { access: {
read: isDeveloper, read: async ({ req }) => {
}, return await hasPermission(req.payload, req.user, "missions:read-sensitive");
},
},
}, },
], ],
}, },

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isAdmin, isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission } from "@/utils/access-control/hasPermission";
export const Technologies: CollectionConfig = { export const Technologies: CollectionConfig = {
slug: "technologies", slug: "technologies",
@ -8,10 +8,10 @@ export const Technologies: CollectionConfig = {
useAsTitle: "name", useAsTitle: "name",
}, },
access: { access: {
create: isAdmin, create: requirePermission("technologies:create"),
update: isAdmin, update: requirePermission("technologies:update"),
delete: isAdmin, delete: requirePermission("technologies:delete"),
read: isAdmin, read: requirePermission("technologies:read"),
}, },
fields: [ fields: [
{ {
@ -31,14 +31,15 @@ export const Technologies: CollectionConfig = {
{ label: "Revision Requested", value: "revision_requested" }, { label: "Revision Requested", value: "revision_requested" },
], ],
defaultValue: "in_progress", defaultValue: "in_progress",
filterOptions: ({ options, data, req }) => { filterOptions: ({ options, req }) => {
return !isDeveloper({ req }) const roles = (req.user?.roles as string[] | undefined) ?? [];
? options.filter((option) => const canReadAll = roles.includes("admin") || roles.includes("developer");
typeof option === "string" if (canReadAll) return options;
? options return options.filter((option) =>
: ["in_progress", "ready_for_review"].includes(option.value), typeof option === "string"
) ? options
: options; : ["in_progress", "ready_for_review"].includes(option.value),
);
}, },
required: true, required: true,
admin: { admin: {

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission, hasPermission } from "@/utils/access-control/hasPermission";
import { LOADOUT_SLOTS } from "@/lib/locker"; import { LOADOUT_SLOTS } from "@/lib/locker";
export const Loadouts: CollectionConfig = { export const Loadouts: CollectionConfig = {
@ -10,21 +10,20 @@ export const Loadouts: CollectionConfig = {
defaultColumns: ["name", "ownerUser", "updatedAt"], defaultColumns: ["name", "ownerUser", "updatedAt"],
}, },
access: { access: {
read: ({ req }) => { read: async ({ req }) => {
if (!req.user) return false; if (!req.user) return false;
if (isDeveloper({ req })) return true; if (await hasPermission(req.payload, req.user, "loadouts:read")) return true;
return { ownerUser: { equals: req.user.id } }; return { ownerUser: { equals: req.user.id } };
}, },
create: ({ req }) => !!req.user, create: ({ req }) => !!req.user,
update: ({ req }) => { update: async ({ req }) => {
if (!req.user) return false; if (!req.user) return false;
if (isDeveloper({ req })) return true; if (await hasPermission(req.payload, req.user, "loadouts:update")) return true;
return { ownerUser: { equals: req.user.id } }; return { ownerUser: { equals: req.user.id } };
}, },
delete: ({ req }) => { delete: async ({ req }) => {
if (!req.user) return false; if (!req.user) return false;
if (isDeveloper({ req })) return true; return await hasPermission(req.payload, req.user, "loadouts:delete");
return { ownerUser: { equals: req.user.id } };
}, },
}, },
fields: [ fields: [

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission, hasPermission } from "@/utils/access-control/hasPermission";
export const LockerStorages: CollectionConfig = { export const LockerStorages: CollectionConfig = {
slug: "locker-storages", slug: "locker-storages",
@ -9,18 +9,23 @@ export const LockerStorages: CollectionConfig = {
defaultColumns: ["name", "ownerUser", "itemCount"], defaultColumns: ["name", "ownerUser", "itemCount"],
}, },
access: { access: {
read: ({ req }) => { read: async ({ req }) => {
if (!req.user) return false; if (!req.user) return false;
if (isDeveloper({ req })) return true; if (await hasPermission(req.payload, req.user, "locker-storages:read")) return true;
return { ownerUser: { equals: req.user.id } }; return { ownerUser: { equals: req.user.id } };
}, },
create: ({ req }) => !!req.user && isDeveloper({ req }), create: ({ req }) => {
update: ({ req }) => {
if (!req.user) return false; if (!req.user) return false;
if (isDeveloper({ req })) return true; return hasPermission(req.payload, req.user, "locker-storages:create");
},
update: async ({ req }) => {
if (!req.user) return false;
if (await hasPermission(req.payload, req.user, "locker-storages:update")) return true;
return { ownerUser: { equals: req.user.id } }; return { ownerUser: { equals: req.user.id } };
}, },
delete: isDeveloper, delete: async ({ req }) => {
return await hasPermission(req.payload, req.user, "locker-storages:delete");
},
}, },
fields: [ fields: [
{ {

View file

@ -1,5 +1,6 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { isDeveloper } from "@/utils/access-control/isRole";
import { requirePermission } from "@/utils/access-control/hasPermission";
export const Assets: CollectionConfig = { export const Assets: CollectionConfig = {
slug: "assets", slug: "assets",
@ -595,9 +596,9 @@ export const Assets: CollectionConfig = {
condition: ({ user }) => isDeveloper({ req: { user: user } } as never), condition: ({ user }) => isDeveloper({ req: { user: user } } as never),
}, },
access: { access: {
read: isDeveloper, read: requirePermission("assets:read"),
create: isDeveloper, create: requirePermission("assets:create"),
update: isDeveloper, update: requirePermission("assets:update"),
}, },
fields: [ fields: [
{ {

View file

@ -1,6 +1,5 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission, hasPermission } from "@/utils/access-control/hasPermission";
import hasRoles from "@/utils/access-control/hasRoles";
export const Shipments: CollectionConfig = { export const Shipments: CollectionConfig = {
slug: "shipments", slug: "shipments",
@ -11,17 +10,15 @@ export const Shipments: CollectionConfig = {
}, },
access: { access: {
read: ({ req }) => !!req.user, read: ({ req }) => !!req.user,
create: ({ req }) => { create: async ({ req }) => {
if (!req.user) return false; if (!req.user) return false;
if (hasRoles(["developer", "admin"], req.user)) return true; return hasPermission(req.payload, req.user, "shipments:create");
return hasRoles(["user"], req.user);
}, },
update: ({ req }) => { update: async ({ req }) => {
if (!req.user) return false; if (!req.user) return false;
if (hasRoles(["developer", "admin"], req.user)) return true; return hasPermission(req.payload, req.user, "shipments:update");
return hasRoles(["user"], req.user);
}, },
delete: isDeveloper, delete: requirePermission("shipments:delete"),
}, },
fields: [ fields: [
{ {

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission, hasPermission } from "@/utils/access-control/hasPermission";
export const MarketListings: CollectionConfig = { export const MarketListings: CollectionConfig = {
slug: "market-listings", slug: "market-listings",
@ -13,14 +13,12 @@ export const MarketListings: CollectionConfig = {
access: { access: {
read: ({ req }) => !!req.user, read: ({ req }) => !!req.user,
create: ({ req }) => !!req.user, create: ({ req }) => !!req.user,
update: ({ req }) => { update: async ({ req }) => {
if (!req.user) return false; if (!req.user) return false;
if (isDeveloper({ req })) return true; if (await hasPermission(req.payload, req.user, "market-listings:update")) return true;
return { return { seller: { equals: req.user.id } };
seller: { equals: req.user.id },
};
}, },
delete: isDeveloper, delete: requirePermission("market-listings:delete"),
}, },
fields: [ fields: [
{ {

View file

@ -1,7 +1,6 @@
import { CollectionConfig, Where } from "payload"; import { CollectionConfig, Where } from "payload";
import type { User } from "@/payload-types"; import type { User } from "@/payload-types";
import hasRoles from "@/utils/access-control/hasRoles"; import { requirePermission, hasPermission } from "@/utils/access-control/hasPermission";
import { isDeveloper } from "@/utils/access-control/isRole";
export const MarketNegotiations: CollectionConfig = { export const MarketNegotiations: CollectionConfig = {
slug: "market-negotiations", slug: "market-negotiations",
@ -21,24 +20,24 @@ export const MarketNegotiations: CollectionConfig = {
pagination: { defaultLimit: 50 }, pagination: { defaultLimit: 50 },
}, },
access: { access: {
read: ({ req }) => { read: async ({ req }) => {
const user = req.user as User | null; const user = req.user as User | null;
if (!user) return false; if (!user) return false;
if (hasRoles(["admin", "developer"], user)) return true; if (await hasPermission(req.payload, user, "market-negotiations:read")) return true;
return { return {
or: [{ buyer: { equals: user.id } }, { "listing.seller": { equals: user.id } }], or: [{ buyer: { equals: user.id } }, { "listing.seller": { equals: user.id } }],
} as Where; } as Where;
}, },
create: ({ req }) => !!req.user, create: ({ req }) => !!req.user,
update: ({ req }) => { update: async ({ req }) => {
const user = req.user as User | null; const user = req.user as User | null;
if (!user) return false; if (!user) return false;
if (hasRoles(["admin", "developer"], user)) return true; if (await hasPermission(req.payload, user, "market-negotiations:update")) return true;
return { return {
or: [{ buyer: { equals: user.id } }, { "listing.seller": { equals: user.id } }], or: [{ buyer: { equals: user.id } }, { "listing.seller": { equals: user.id } }],
} as Where; } as Where;
}, },
delete: isDeveloper, delete: requirePermission("market-negotiations:delete"),
}, },
fields: [ fields: [
{ {

View file

@ -1,13 +1,13 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission } from "@/utils/access-control/hasPermission";
export const MissionFiles: CollectionConfig = { export const MissionFiles: CollectionConfig = {
slug: "mission-files", slug: "mission-files",
access: { access: {
create: isDeveloper, create: requirePermission("mission-files:create"),
update: isDeveloper, update: requirePermission("mission-files:update"),
delete: isDeveloper, delete: requirePermission("mission-files:delete"),
read: isDeveloper, read: requirePermission("mission-files:read"),
}, },
admin: { admin: {
group: "Server", group: "Server",

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission } from "@/utils/access-control/hasPermission";
export const ModLists: CollectionConfig = { export const ModLists: CollectionConfig = {
slug: "mod-lists", slug: "mod-lists",
@ -7,10 +7,10 @@ export const ModLists: CollectionConfig = {
group: "Server", group: "Server",
}, },
access: { access: {
create: isDeveloper, create: requirePermission("mod-lists:create"),
update: isDeveloper, update: requirePermission("mod-lists:update"),
delete: isDeveloper, delete: requirePermission("mod-lists:delete"),
read: isDeveloper, read: requirePermission("mod-lists:read"),
}, },
fields: [ fields: [
{ {

View file

@ -1,6 +1,5 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission, hasPermission } from "@/utils/access-control/hasPermission";
import hasRoles from "@/utils/access-control/hasRoles";
import { import {
CLOSING_STATUSES, CLOSING_STATUSES,
STATUS_LABEL, STATUS_LABEL,
@ -49,17 +48,19 @@ export const Tickets: CollectionConfig = {
pagination: { defaultLimit: 50 }, pagination: { defaultLimit: 50 },
}, },
access: { access: {
read: ({ req }) => { read: async ({ req }) => {
if (!req.user) return false; if (!req.user) return false;
if (hasRoles(["admin", "developer"], req.user)) return true; if (await hasPermission(req.payload, req.user, "tickets:read")) return true;
return { reporter: { equals: req.user.id } }; return { reporter: { equals: req.user.id } };
}, },
create: ({ req }) => !!req.user, create: ({ req }) => !!req.user,
update: ({ req }) => { update: async ({ req }) => {
if (!req.user) return false; if (!req.user) return false;
return hasRoles(["admin", "developer"], req.user) ? true : false; return await hasPermission(req.payload, req.user, "tickets:update");
},
delete: async ({ req }) => {
return await hasPermission(req.payload, req.user, "tickets:delete");
}, },
delete: isDeveloper,
}, },
hooks: { hooks: {
beforeChange: [ beforeChange: [

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission } from "@/utils/access-control/hasPermission";
export const Assignments: CollectionConfig = { export const Assignments: CollectionConfig = {
slug: "assignments", slug: "assignments",
@ -8,9 +8,9 @@ export const Assignments: CollectionConfig = {
useAsTitle: "name", useAsTitle: "name",
}, },
access: { access: {
create: isDeveloper, create: requirePermission("assignments:create"),
update: isDeveloper, update: requirePermission("assignments:update"),
delete: isDeveloper, delete: requirePermission("assignments:delete"),
}, },
fields: [ fields: [
{ {

View file

@ -1,12 +1,12 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission } from "@/utils/access-control/hasPermission";
export const Awards: CollectionConfig = { export const Awards: CollectionConfig = {
slug: "awards", slug: "awards",
access: { access: {
create: isDeveloper, create: requirePermission("awards:create"),
update: isDeveloper, update: requirePermission("awards:update"),
delete: isDeveloper, delete: requirePermission("awards:delete"),
}, },
admin: { admin: {
useAsTitle: "name", useAsTitle: "name",

View file

@ -1,12 +1,12 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission } from "@/utils/access-control/hasPermission";
export const Experience: CollectionConfig = { export const Experience: CollectionConfig = {
slug: "experience", slug: "experience",
access: { access: {
create: isDeveloper, create: requirePermission("experience:create"),
update: isDeveloper, update: requirePermission("experience:update"),
delete: isDeveloper, delete: requirePermission("experience:delete"),
}, },
admin: { admin: {
group: "Users", group: "Users",

View file

@ -1,7 +1,6 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { Award } from "@/payload-types"; import { Award } from "@/payload-types";
import hasRoles from "@/utils/access-control/hasRoles"; import { requirePermission, hasPermission } from "@/utils/access-control/hasPermission";
import { isDeveloper } from "@/utils/access-control/isRole";
export const Profiles: CollectionConfig = { export const Profiles: CollectionConfig = {
slug: "profiles", slug: "profiles",
@ -11,10 +10,18 @@ export const Profiles: CollectionConfig = {
defaultColumns: ["profileTitle", "user", "rank"], defaultColumns: ["profileTitle", "user", "rank"],
}, },
access: { access: {
read: (acl) => hasRoles(["user"], acl.req.user), read: async ({ req }) => {
create: isDeveloper, return await hasPermission(req.payload, req.user, "profiles:read");
update: isDeveloper, },
delete: isDeveloper, create: async ({ req }) => {
return await hasPermission(req.payload, req.user, "profiles:create");
},
update: async ({ req }) => {
return await hasPermission(req.payload, req.user, "profiles:update");
},
delete: async ({ req }) => {
return await hasPermission(req.payload, req.user, "profiles:delete");
},
}, },
fields: [ fields: [
{ {
@ -43,6 +50,12 @@ export const Profiles: CollectionConfig = {
label: "Personnel Dossier", label: "Personnel Dossier",
type: "group", type: "group",
fields: [ fields: [
{
name: "enlistmentDate",
type: "date",
required: true,
defaultValue: ({ user }) => user?.createdAt,
},
{ {
name: "personalExcerpt", name: "personalExcerpt",
type: "textarea", type: "textarea",
@ -51,6 +64,14 @@ export const Profiles: CollectionConfig = {
name: "intelExcerpt", name: "intelExcerpt",
label: "Intelligence Record", label: "Intelligence Record",
type: "richText", type: "richText",
access: {
create: async ({ req }) => {
return await hasPermission(req.payload, req.user, "profiles:intel_excerpt:update");
},
update: async ({ req }) => {
return await hasPermission(req.payload, req.user, "profiles:intel_excerpt:update");
},
},
}, },
], ],
}, },

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission } from "@/utils/access-control/hasPermission";
export const Qualifications: CollectionConfig = { export const Qualifications: CollectionConfig = {
slug: "qualifications", slug: "qualifications",
@ -7,9 +7,9 @@ export const Qualifications: CollectionConfig = {
group: "Users", group: "Users",
}, },
access: { access: {
create: isDeveloper, create: requirePermission("qualifications:create"),
update: isDeveloper, update: requirePermission("qualifications:update"),
delete: isDeveloper, delete: requirePermission("qualifications:delete"),
}, },
fields: [ fields: [
{ {

View file

@ -1,12 +1,12 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission } from "@/utils/access-control/hasPermission";
export const Ranks: CollectionConfig = { export const Ranks: CollectionConfig = {
slug: "ranks", slug: "ranks",
access: { access: {
create: isDeveloper, create: requirePermission("ranks:create"),
update: isDeveloper, update: requirePermission("ranks:update"),
delete: isDeveloper, delete: requirePermission("ranks:delete"),
}, },
admin: { admin: {
useAsTitle: "name", useAsTitle: "name",

View file

@ -1,7 +1,6 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import type { User } from "@/payload-types"; import type { User } from "@/payload-types";
import hasRoles from "@/utils/access-control/hasRoles"; import { requirePermission, hasPermission } from "@/utils/access-control/hasPermission";
import { isDeveloper } from "@/utils/access-control/isRole";
export const UserNotifications: CollectionConfig = { export const UserNotifications: CollectionConfig = {
slug: "user-notifications", slug: "user-notifications",
@ -13,20 +12,20 @@ export const UserNotifications: CollectionConfig = {
pagination: { defaultLimit: 25 }, pagination: { defaultLimit: 25 },
}, },
access: { access: {
read: ({ req }) => { read: async ({ req }) => {
const user = req.user as User | null; const user = req.user as User | null;
if (!user) return false; if (!user) return false;
if (hasRoles(["admin", "developer"], user)) return true; if (await hasPermission(req.payload, user, "user-notifications:read")) return true;
return { user: { equals: user.id } }; return { user: { equals: user.id } };
}, },
create: isDeveloper, create: requirePermission("user-notifications:create"),
update: ({ req }) => { update: async ({ req }) => {
const user = req.user as User | null; const user = req.user as User | null;
if (!user) return false; if (!user) return false;
if (hasRoles(["admin", "developer"], user)) return true; if (await hasPermission(req.payload, user, "user-notifications:update")) return true;
return { user: { equals: user.id } }; return { user: { equals: user.id } };
}, },
delete: isDeveloper, delete: requirePermission("user-notifications:delete"),
}, },
fields: [ fields: [
{ {

View file

@ -1,5 +1,5 @@
import type { CollectionConfig } from "payload"; import type { CollectionConfig } from "payload";
import { isAdmin, isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission, hasPermission, isSuperuser } from "@/utils/access-control/hasPermission";
import { ensurePersonalAccount } from "@/lib/banking/index"; import { ensurePersonalAccount } from "@/lib/banking/index";
import { MUTEABLE_NOTIFICATION_TYPES } from "@/lib/notifications/notificationTypes"; import { MUTEABLE_NOTIFICATION_TYPES } from "@/lib/notifications/notificationTypes";
@ -20,25 +20,45 @@ export const Users: CollectionConfig = {
); );
} }
const ranks = await payload.find({ let recruit: { id: number } | undefined;
collection: "ranks",
where: { try {
name: { const ranks = await payload.find({
like: "Recruit", collection: "ranks",
where: {
name: {
like: "Recruit",
},
}, },
}, limit: 1,
limit: 1, depth: 0,
depth: 0, overrideAccess: true,
overrideAccess: true, });
}); recruit = (ranks as { docs: Array<{ id: number }> }).docs[0];
const recruit = (ranks as { docs: Array<{ id: number }> }).docs[0];
if (!recruit) {
const created = await payload.create({
collection: "ranks",
data: {
name: "Recruit",
abbreviation: "Rct",
description: "Entry-level rank for new members.",
},
overrideAccess: true,
});
recruit = { id: created.id };
payload.logger.info("[Users] Created default 'Recruit' rank on demand.");
}
} catch (e) {
payload.logger.error(`[Users] Failed to resolve Recruit rank: ${e}`);
}
try { try {
await payload.create({ await payload.create({
collection: "profiles", collection: "profiles",
data: { data: {
user: userId, user: userId,
rank: recruit?.id ?? null, rank: recruit?.id ?? (null as unknown as number),
progression: { progression: {
qualifications: [], qualifications: [],
experience: 0, experience: 0,
@ -57,22 +77,42 @@ export const Users: CollectionConfig = {
}, },
slug: "users", slug: "users",
access: { access: {
admin: isAdmin, admin: requirePermission("system:admin-access"),
unlock: isDeveloper, unlock: requirePermission("users:unlock"),
create: isDeveloper, create: requirePermission("users:create"),
update: ({ req, data }) => { update: async ({ req, id, data }) => {
const isEditingDeveloper = data?.roles?.includes("developer"); if (data?.roles?.includes("developer")) {
if (isEditingDeveloper) { return isSuperuser(req.payload, req.user);
return isDeveloper({ req });
} }
return isAdmin({ req }); const existing = id
? ((await req.payload.findByID({
collection: "users",
id,
depth: 2,
overrideAccess: true,
})) as { roleDocs?: Array<{ isSuperuser?: boolean }> | null })
: null;
if (existing?.roleDocs?.some((r) => r.isSuperuser)) {
return isSuperuser(req.payload, req.user);
}
return hasPermission(req.payload, req.user, "users:update");
}, },
delete: ({ req, data }) => { delete: async ({ req, id, data }) => {
const isEditingDeveloper = data?.roles?.includes("developer"); if (data?.roles?.includes("developer")) {
if (isEditingDeveloper) { return isSuperuser(req.payload, req.user);
return isDeveloper({ req });
} }
return isAdmin({ req }); const existing = id
? ((await req.payload.findByID({
collection: "users",
id,
depth: 2,
overrideAccess: true,
})) as { roleDocs?: Array<{ isSuperuser?: boolean }> | null })
: null;
if (existing?.roleDocs?.some((r) => r.isSuperuser)) {
return isSuperuser(req.payload, req.user);
}
return hasPermission(req.payload, req.user, "users:delete");
}, },
}, },
admin: { admin: {
@ -140,8 +180,8 @@ export const Users: CollectionConfig = {
name: "roles", name: "roles",
type: "select", type: "select",
access: { access: {
create: isDeveloper, create: requirePermission("users:assign-roles"),
update: isDeveloper, update: requirePermission("users:assign-roles"),
}, },
hasMany: true, hasMany: true,
options: [ options: [
@ -173,6 +213,22 @@ export const Users: CollectionConfig = {
) )
: options; : options;
},*/ },*/
admin: {
description: "Legacy role enum — being replaced by the dynamic RBAC system (see 'Role Assignments' below).",
},
},
{
name: "roleDocs",
type: "relationship",
relationTo: "roles",
hasMany: true,
access: {
create: requirePermission("users:assign-roles"),
update: requirePermission("users:assign-roles"),
},
admin: {
description: "Dynamic RBAC role assignments. These determine the user's permissions via the roles collection.",
},
}, },
{ {
name: "preferences", name: "preferences",

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission } from "@/utils/access-control/hasPermission";
export const Maps: CollectionConfig = { export const Maps: CollectionConfig = {
slug: "maps", slug: "maps",
@ -8,10 +8,10 @@ export const Maps: CollectionConfig = {
useAsTitle: "name", useAsTitle: "name",
}, },
access: { access: {
create: isDeveloper, create: requirePermission("maps:create"),
update: isDeveloper, update: requirePermission("maps:update"),
delete: isDeveloper, delete: requirePermission("maps:delete"),
read: isDeveloper, read: requirePermission("maps:read"),
}, },
fields: [ fields: [
{ {

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from "payload"; import { CollectionConfig } from "payload";
import { isDeveloper } from "@/utils/access-control/isRole"; import { requirePermission } from "@/utils/access-control/hasPermission";
export const NarrativeEvents: CollectionConfig = { export const NarrativeEvents: CollectionConfig = {
slug: "narrative-events", slug: "narrative-events",
@ -8,10 +8,10 @@ export const NarrativeEvents: CollectionConfig = {
useAsTitle: "name", useAsTitle: "name",
}, },
access: { access: {
create: isDeveloper, create: requirePermission("narrative-events:create"),
update: isDeveloper, update: requirePermission("narrative-events:update"),
delete: isDeveloper, delete: requirePermission("narrative-events:delete"),
read: isDeveloper, read: requirePermission("narrative-events:read"),
}, },
fields: [ fields: [
{ {

View file

@ -74,6 +74,7 @@ export interface Config {
'game-npcs': GameNpc; 'game-npcs': GameNpc;
'game-event-logs': GameEventLog; 'game-event-logs': GameEventLog;
users: User; users: User;
roles: Role;
ranks: Rank; ranks: Rank;
profiles: Profile; profiles: Profile;
awards: Award; awards: Award;
@ -125,6 +126,7 @@ export interface Config {
'game-npcs': GameNpcsSelect<false> | GameNpcsSelect<true>; 'game-npcs': GameNpcsSelect<false> | GameNpcsSelect<true>;
'game-event-logs': GameEventLogsSelect<false> | GameEventLogsSelect<true>; 'game-event-logs': GameEventLogsSelect<false> | GameEventLogsSelect<true>;
users: UsersSelect<false> | UsersSelect<true>; users: UsersSelect<false> | UsersSelect<true>;
roles: RolesSelect<false> | RolesSelect<true>;
ranks: RanksSelect<false> | RanksSelect<true>; ranks: RanksSelect<false> | RanksSelect<true>;
profiles: ProfilesSelect<false> | ProfilesSelect<true>; profiles: ProfilesSelect<false> | ProfilesSelect<true>;
awards: AwardsSelect<false> | AwardsSelect<true>; awards: AwardsSelect<false> | AwardsSelect<true>;
@ -1260,7 +1262,14 @@ export interface User {
displayName: string; displayName: string;
payloadDisplayName?: string | null; payloadDisplayName?: string | null;
steamId: string; steamId: string;
/**
* Legacy role enum — being replaced by the dynamic RBAC system (see 'Role Assignments' below).
*/
roles?: ('guest' | 'user' | 'trusted' | 'admin' | 'developer')[] | null; roles?: ('guest' | 'user' | 'trusted' | 'admin' | 'developer')[] | null;
/**
* Dynamic RBAC role assignments. These determine the user's permissions via the roles collection.
*/
roleDocs?: (number | Role)[] | null;
preferences?: { preferences?: {
notifications?: { notifications?: {
mutedAll?: boolean | null; mutedAll?: boolean | null;
@ -1335,6 +1344,203 @@ export interface User {
password?: string | null; password?: string | null;
collection: 'users'; collection: 'users';
} }
/**
* Dynamic roles for the RBAC permission system. Assign permissions to roles, then assign roles to users.
*
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "roles".
*/
export interface Role {
id: number;
/**
* Display name for this role, e.g. 'Logistics Officer'.
*/
name: string;
/**
* Machine-readable key. Built-in roles: 'guest', 'user', 'admin', 'developer'. Used by seed scripts and the Discord bot to look up roles by key.
*/
slug: string;
/**
* Optional notes about what this role is for.
*/
description?: string | null;
/**
* Permissions granted by this role. The full list is defined in src/permissions/index.ts.
*/
permissions?:
| (
| 'system:admin-access'
| 'users:create'
| 'users:read'
| 'users:update'
| 'users:delete'
| 'users:unlock'
| 'users:assign-roles'
| 'ranks:create'
| 'ranks:read'
| 'ranks:update'
| 'ranks:delete'
| 'profiles:create'
| 'profiles:read'
| 'profiles:update'
| 'profiles:delete'
| 'awards:create'
| 'awards:read'
| 'awards:update'
| 'awards:delete'
| 'qualifications:create'
| 'qualifications:read'
| 'qualifications:update'
| 'qualifications:delete'
| 'assignments:create'
| 'assignments:read'
| 'assignments:update'
| 'assignments:delete'
| 'experience:create'
| 'experience:read'
| 'experience:update'
| 'experience:delete'
| 'user-notifications:create'
| 'user-notifications:read'
| 'user-notifications:update'
| 'user-notifications:delete'
| 'missions:create'
| 'missions:read'
| 'missions:update'
| 'missions:delete'
| 'missions:read-sensitive'
| 'mission-attendances:create'
| 'mission-attendances:read'
| 'mission-attendances:update'
| 'mission-attendances:delete'
| 'campaigns:create'
| 'campaigns:read'
| 'campaigns:update'
| 'campaigns:delete'
| 'factions:create'
| 'factions:read'
| 'factions:update'
| 'factions:delete'
| 'technologies:create'
| 'technologies:read'
| 'technologies:update'
| 'technologies:delete'
| 'assets:create'
| 'assets:read'
| 'assets:update'
| 'assets:delete'
| 'resources:create'
| 'resources:read'
| 'resources:update'
| 'resources:delete'
| 'vehicles:create'
| 'vehicles:read'
| 'vehicles:update'
| 'vehicles:delete'
| 'structures:create'
| 'structures:read'
| 'structures:update'
| 'structures:delete'
| 'shipments:create'
| 'shipments:read'
| 'shipments:update'
| 'shipments:delete'
| 'bank-accounts:create'
| 'bank-accounts:read'
| 'bank-accounts:update'
| 'bank-accounts:delete'
| 'bank-transactions:create'
| 'bank-transactions:read'
| 'bank-transactions:update'
| 'bank-transactions:delete'
| 'ledger-entries:create'
| 'ledger-entries:read'
| 'ledger-entries:update'
| 'ledger-entries:delete'
| 'locker-storages:create'
| 'locker-storages:read'
| 'locker-storages:update'
| 'locker-storages:delete'
| 'loadouts:create'
| 'loadouts:read'
| 'loadouts:update'
| 'loadouts:delete'
| 'market-listings:create'
| 'market-listings:read'
| 'market-listings:update'
| 'market-listings:delete'
| 'market-negotiations:create'
| 'market-negotiations:read'
| 'market-negotiations:update'
| 'market-negotiations:delete'
| 'tickets:create'
| 'tickets:read'
| 'tickets:update'
| 'tickets:delete'
| 'game-structures:create'
| 'game-structures:read'
| 'game-structures:update'
| 'game-structures:delete'
| 'game-vehicles:create'
| 'game-vehicles:read'
| 'game-vehicles:update'
| 'game-vehicles:delete'
| 'game-npcs:create'
| 'game-npcs:read'
| 'game-npcs:update'
| 'game-npcs:delete'
| 'game-hard-resources:create'
| 'game-hard-resources:read'
| 'game-hard-resources:update'
| 'game-hard-resources:delete'
| 'game-event-logs:create'
| 'game-event-logs:read'
| 'game-event-logs:update'
| 'game-event-logs:delete'
| 'maps:create'
| 'maps:read'
| 'maps:update'
| 'maps:delete'
| 'narrative-events:create'
| 'narrative-events:read'
| 'narrative-events:update'
| 'narrative-events:delete'
| 'mission-files:create'
| 'mission-files:read'
| 'mission-files:update'
| 'mission-files:delete'
| 'mod-lists:create'
| 'mod-lists:read'
| 'mod-lists:update'
| 'mod-lists:delete'
| 'game-rules:read'
| 'game-rules:update'
| 'shims:read'
| 'shims:update'
| 'logistics:manage'
| 'intelligence:manage'
| 'profiles:intel_excerpt:update'
| 'banking:manage'
| 'tickets:staff'
| 'discord:staff'
| 'discord:announce'
)[]
| null;
/**
* Parent roles to inherit permissions and settings from. Inheriting from a superuser role makes this role a superuser. Restricted to developers.
*/
parentRoles?: (number | Role)[] | null;
/**
* System roles are built-in and cannot be deleted. They can still be edited (e.g. to change their permissions).
*/
isSystem?: boolean | null;
/**
* Superuser roles bypass ALL permission checks. Use with extreme caution — this grants unrestricted access.
*/
isSuperuser?: boolean | null;
updatedAt: string;
createdAt: string;
}
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "ranks". * via the `definition` "ranks".
@ -1358,7 +1564,8 @@ export interface Profile {
user: number | User; user: number | User;
rank: number | Rank; rank: number | Rank;
profileTitle?: string | null; profileTitle?: string | null;
dossier?: { dossier: {
enlistmentDate: string;
personalExcerpt?: string | null; personalExcerpt?: string | null;
intelExcerpt?: { intelExcerpt?: {
root: { root: {
@ -2438,6 +2645,10 @@ export interface PayloadLockedDocument {
relationTo: 'users'; relationTo: 'users';
value: number | User; value: number | User;
} | null) } | null)
| ({
relationTo: 'roles';
value: number | Role;
} | null)
| ({ | ({
relationTo: 'ranks'; relationTo: 'ranks';
value: number | Rank; value: number | Rank;
@ -2746,6 +2957,7 @@ export interface UsersSelect<T extends boolean = true> {
payloadDisplayName?: T; payloadDisplayName?: T;
steamId?: T; steamId?: T;
roles?: T; roles?: T;
roleDocs?: T;
preferences?: preferences?:
| T | T
| { | {
@ -2786,6 +2998,21 @@ export interface UsersSelect<T extends boolean = true> {
expiresAt?: T; expiresAt?: T;
}; };
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "roles_select".
*/
export interface RolesSelect<T extends boolean = true> {
name?: T;
slug?: T;
description?: T;
permissions?: T;
parentRoles?: T;
isSystem?: T;
isSuperuser?: T;
updatedAt?: T;
createdAt?: T;
}
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "ranks_select". * via the `definition` "ranks_select".
@ -2810,6 +3037,7 @@ export interface ProfilesSelect<T extends boolean = true> {
dossier?: dossier?:
| T | T
| { | {
enlistmentDate?: T;
personalExcerpt?: T; personalExcerpt?: T;
intelExcerpt?: T; intelExcerpt?: T;
}; };