Compare commits
6 commits
b4bb328acd
...
2a1c94072f
| Author | SHA1 | Date | |
|---|---|---|---|
| 2a1c94072f | |||
| 9841fb3628 | |||
| 8cd5d22a47 | |||
| 37d82fd711 | |||
| 822ac47c23 | |||
| e29b403d06 |
11 changed files with 21379 additions and 111 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
import type { CollectionConfig } from "payload";
|
import type { CollectionConfig } from "payload";
|
||||||
import { isAdmin, isDeveloper } from "@/utils/access-control/isRole";
|
import { isAdmin, isDeveloper } from "@/utils/access-control/isRole";
|
||||||
|
import { requirePermission } from "@/utils/access-control/hasPermission";
|
||||||
import { permissionSelectOptions } from "@/permissions";
|
import { permissionSelectOptions } from "@/permissions";
|
||||||
import { invalidatePermissionCache } from "@/utils/access-control/loadUserPermissions";
|
import { invalidatePermissionCache } from "@/utils/access-control/loadUserPermissions";
|
||||||
|
|
||||||
|
|
@ -12,7 +13,7 @@ export const Roles: CollectionConfig = {
|
||||||
"Dynamic roles for the RBAC permission system. Assign permissions to roles, then assign roles to users.",
|
"Dynamic roles for the RBAC permission system. Assign permissions to roles, then assign roles to users.",
|
||||||
},
|
},
|
||||||
access: {
|
access: {
|
||||||
admin: isAdmin,
|
admin: requirePermission("admin:roles:manage"),
|
||||||
create: isAdmin,
|
create: isAdmin,
|
||||||
update: isAdmin,
|
update: isAdmin,
|
||||||
delete: isDeveloper,
|
delete: isDeveloper,
|
||||||
|
|
|
||||||
20596
src/migrations/20260901_222200_add_admin_page_permissions.json
Normal file
20596
src/migrations/20260901_222200_add_admin_page_permissions.json
Normal file
File diff suppressed because it is too large
Load diff
50
src/migrations/20260901_222200_add_admin_page_permissions.ts
Normal file
50
src/migrations/20260901_222200_add_admin_page_permissions.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
|
||||||
|
|
||||||
|
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
|
||||||
|
await db.execute(sql`
|
||||||
|
DO $$
|
||||||
|
DECLARE permission text;
|
||||||
|
BEGIN
|
||||||
|
FOREACH permission IN ARRAY ARRAY[
|
||||||
|
'media:create', 'media:read', 'media:update', 'media:delete', 'admin:media:manage',
|
||||||
|
'admin:users:manage', 'roles:create', 'roles:read', 'roles:update', 'roles:delete',
|
||||||
|
'admin:roles:manage', 'admin:ranks:manage', 'admin:profiles:manage', 'admin:awards:manage',
|
||||||
|
'admin:qualifications:manage', 'admin:assignments:manage', 'assignment-transfers:create',
|
||||||
|
'assignment-transfers:read', 'assignment-transfers:update', 'assignment-transfers:delete',
|
||||||
|
'admin:assignment-transfers:manage', 'admin:experience:manage',
|
||||||
|
'admin:user-notifications:manage', 'admin:evaluations:manage', 'admin:missions:manage',
|
||||||
|
'admin:mission-attendances:manage', 'admin:campaigns:manage', 'admin:factions:manage',
|
||||||
|
'admin:technologies:manage', 'admin:assets:manage', 'admin:resources:manage',
|
||||||
|
'admin:vehicles:manage', 'admin:structures:manage', 'admin:shipments:manage',
|
||||||
|
'admin:bank-accounts:manage', 'admin:bank-transactions:manage', 'admin:ledger-entries:manage',
|
||||||
|
'admin:locker-storages:manage', 'admin:loadouts:manage', 'admin:market-listings:manage',
|
||||||
|
'admin:market-negotiations:manage', 'admin:projects:manage', 'admin:labels:manage',
|
||||||
|
'admin:sprints:manage', 'admin:releases:manage', 'admin:tickets:manage',
|
||||||
|
'ticket-votes:create', 'ticket-votes:read', 'ticket-votes:update', 'ticket-votes:delete',
|
||||||
|
'admin:ticket-votes:manage', 'admin:game-structures:manage', 'admin:game-vehicles:manage',
|
||||||
|
'admin:game-npcs:manage', 'admin:game-hard-resources:manage', 'admin:game-event-logs:manage',
|
||||||
|
'admin:maps:manage', 'admin:narrative-events:manage', 'admin:mission-files:manage',
|
||||||
|
'admin:mod-lists:manage', 'game-servers:create', 'game-servers:read', 'game-servers:update',
|
||||||
|
'game-servers:delete', 'admin:game-servers:manage', 'arma-sync-events:create',
|
||||||
|
'arma-sync-events:read', 'arma-sync-events:update', 'arma-sync-events:delete',
|
||||||
|
'admin:arma-sync-events:manage', 'arma-commands:create', 'arma-commands:read',
|
||||||
|
'arma-commands:update', 'arma-commands:delete', 'admin:arma-commands:manage'
|
||||||
|
] LOOP
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM pg_enum e
|
||||||
|
JOIN pg_type t ON t.oid = e.enumtypid
|
||||||
|
WHERE t.typname = 'enum_roles_permissions' AND e.enumlabel = permission
|
||||||
|
) THEN
|
||||||
|
EXECUTE format('ALTER TYPE public.enum_roles_permissions ADD VALUE %L', permission);
|
||||||
|
END IF;
|
||||||
|
END LOOP;
|
||||||
|
END $$;`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
|
||||||
|
await db.execute(sql`
|
||||||
|
ALTER TABLE "roles_permissions" ALTER COLUMN "value" SET DATA TYPE text;
|
||||||
|
DROP TYPE "public"."enum_roles_permissions";
|
||||||
|
CREATE TYPE "public"."enum_roles_permissions" AS ENUM('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', 'evaluations:create', 'evaluations:read', 'evaluations:update', 'evaluations: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', 'campaigns:manage', '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', 'projects:create', 'projects:read', 'projects:update', 'projects:delete', 'labels:create', 'labels:read', 'labels:update', 'labels:delete', 'sprints:create', 'sprints:read', 'sprints:update', 'sprints:delete', 'releases:create', 'releases:read', 'releases:update', 'releases: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');
|
||||||
|
ALTER TABLE "roles_permissions" ALTER COLUMN "value" SET DATA TYPE "public"."enum_roles_permissions" USING "value"::"public"."enum_roles_permissions";`)
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,7 @@ import * as migration_20260831_142503 from './20260831_142503';
|
||||||
import * as migration_20260831_180233_assignment_transfers from './20260831_180233_assignment_transfers';
|
import * as migration_20260831_180233_assignment_transfers from './20260831_180233_assignment_transfers';
|
||||||
import * as migration_20260901_014006_add_assignment_transfer_event_target from './20260901_014006_add_assignment_transfer_event_target';
|
import * as migration_20260901_014006_add_assignment_transfer_event_target from './20260901_014006_add_assignment_transfer_event_target';
|
||||||
import * as migration_20260901_213108_add_ticket_votes_and_edit_history from './20260901_213108_add_ticket_votes_and_edit_history';
|
import * as migration_20260901_213108_add_ticket_votes_and_edit_history from './20260901_213108_add_ticket_votes_and_edit_history';
|
||||||
|
import * as migration_20260901_222200_add_admin_page_permissions from './20260901_222200_add_admin_page_permissions';
|
||||||
|
|
||||||
export const migrations = [
|
export const migrations = [
|
||||||
{
|
{
|
||||||
|
|
@ -88,6 +89,11 @@ export const migrations = [
|
||||||
{
|
{
|
||||||
up: migration_20260901_213108_add_ticket_votes_and_edit_history.up,
|
up: migration_20260901_213108_add_ticket_votes_and_edit_history.up,
|
||||||
down: migration_20260901_213108_add_ticket_votes_and_edit_history.down,
|
down: migration_20260901_213108_add_ticket_votes_and_edit_history.down,
|
||||||
name: '20260901_213108_add_ticket_votes_and_edit_history'
|
name: '20260901_213108_add_ticket_votes_and_edit_history',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
up: migration_20260901_222200_add_admin_page_permissions.up,
|
||||||
|
down: migration_20260901_222200_add_admin_page_permissions.down,
|
||||||
|
name: '20260901_222200_add_admin_page_permissions'
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -120,170 +120,245 @@ export const enum_users_preferences_display_lead_or_actual = pgEnum(
|
||||||
);
|
);
|
||||||
export const enum_roles_permissions = pgEnum("enum_roles_permissions", [
|
export const enum_roles_permissions = pgEnum("enum_roles_permissions", [
|
||||||
"system:admin-access",
|
"system:admin-access",
|
||||||
|
"media:create",
|
||||||
|
"media:read",
|
||||||
|
"media:update",
|
||||||
|
"media:delete",
|
||||||
|
"admin:media:manage",
|
||||||
"users:create",
|
"users:create",
|
||||||
"users:read",
|
"users:read",
|
||||||
"users:update",
|
"users:update",
|
||||||
"users:delete",
|
"users:delete",
|
||||||
"users:unlock",
|
"users:unlock",
|
||||||
"users:assign-roles",
|
"users:assign-roles",
|
||||||
|
"admin:users:manage",
|
||||||
|
"roles:create",
|
||||||
|
"roles:read",
|
||||||
|
"roles:update",
|
||||||
|
"roles:delete",
|
||||||
|
"admin:roles:manage",
|
||||||
"ranks:create",
|
"ranks:create",
|
||||||
"ranks:read",
|
"ranks:read",
|
||||||
"ranks:update",
|
"ranks:update",
|
||||||
"ranks:delete",
|
"ranks:delete",
|
||||||
|
"admin:ranks:manage",
|
||||||
"profiles:create",
|
"profiles:create",
|
||||||
"profiles:read",
|
"profiles:read",
|
||||||
"profiles:update",
|
"profiles:update",
|
||||||
"profiles:delete",
|
"profiles:delete",
|
||||||
|
"admin:profiles:manage",
|
||||||
"awards:create",
|
"awards:create",
|
||||||
"awards:read",
|
"awards:read",
|
||||||
"awards:update",
|
"awards:update",
|
||||||
"awards:delete",
|
"awards:delete",
|
||||||
|
"admin:awards:manage",
|
||||||
"qualifications:create",
|
"qualifications:create",
|
||||||
"qualifications:read",
|
"qualifications:read",
|
||||||
"qualifications:update",
|
"qualifications:update",
|
||||||
"qualifications:delete",
|
"qualifications:delete",
|
||||||
|
"admin:qualifications:manage",
|
||||||
"assignments:create",
|
"assignments:create",
|
||||||
"assignments:read",
|
"assignments:read",
|
||||||
"assignments:update",
|
"assignments:update",
|
||||||
"assignments:delete",
|
"assignments:delete",
|
||||||
|
"admin:assignments:manage",
|
||||||
|
"assignment-transfers:create",
|
||||||
|
"assignment-transfers:read",
|
||||||
|
"assignment-transfers:update",
|
||||||
|
"assignment-transfers:delete",
|
||||||
|
"admin:assignment-transfers:manage",
|
||||||
"experience:create",
|
"experience:create",
|
||||||
"experience:read",
|
"experience:read",
|
||||||
"experience:update",
|
"experience:update",
|
||||||
"experience:delete",
|
"experience:delete",
|
||||||
|
"admin:experience:manage",
|
||||||
"user-notifications:create",
|
"user-notifications:create",
|
||||||
"user-notifications:read",
|
"user-notifications:read",
|
||||||
"user-notifications:update",
|
"user-notifications:update",
|
||||||
"user-notifications:delete",
|
"user-notifications:delete",
|
||||||
|
"admin:user-notifications:manage",
|
||||||
"evaluations:create",
|
"evaluations:create",
|
||||||
"evaluations:read",
|
"evaluations:read",
|
||||||
"evaluations:update",
|
"evaluations:update",
|
||||||
"evaluations:delete",
|
"evaluations:delete",
|
||||||
|
"admin:evaluations:manage",
|
||||||
"missions:create",
|
"missions:create",
|
||||||
"missions:read",
|
"missions:read",
|
||||||
"missions:update",
|
"missions:update",
|
||||||
"missions:delete",
|
"missions:delete",
|
||||||
"missions:read-sensitive",
|
"missions:read-sensitive",
|
||||||
|
"admin:missions:manage",
|
||||||
"mission-attendances:create",
|
"mission-attendances:create",
|
||||||
"mission-attendances:read",
|
"mission-attendances:read",
|
||||||
"mission-attendances:update",
|
"mission-attendances:update",
|
||||||
"mission-attendances:delete",
|
"mission-attendances:delete",
|
||||||
|
"admin:mission-attendances:manage",
|
||||||
"campaigns:create",
|
"campaigns:create",
|
||||||
"campaigns:read",
|
"campaigns:read",
|
||||||
"campaigns:update",
|
"campaigns:update",
|
||||||
"campaigns:delete",
|
"campaigns:delete",
|
||||||
|
"admin:campaigns:manage",
|
||||||
"campaigns:manage",
|
"campaigns:manage",
|
||||||
"factions:create",
|
"factions:create",
|
||||||
"factions:read",
|
"factions:read",
|
||||||
"factions:update",
|
"factions:update",
|
||||||
"factions:delete",
|
"factions:delete",
|
||||||
|
"admin:factions:manage",
|
||||||
"technologies:create",
|
"technologies:create",
|
||||||
"technologies:read",
|
"technologies:read",
|
||||||
"technologies:update",
|
"technologies:update",
|
||||||
"technologies:delete",
|
"technologies:delete",
|
||||||
|
"admin:technologies:manage",
|
||||||
"assets:create",
|
"assets:create",
|
||||||
"assets:read",
|
"assets:read",
|
||||||
"assets:update",
|
"assets:update",
|
||||||
"assets:delete",
|
"assets:delete",
|
||||||
|
"admin:assets:manage",
|
||||||
"resources:create",
|
"resources:create",
|
||||||
"resources:read",
|
"resources:read",
|
||||||
"resources:update",
|
"resources:update",
|
||||||
"resources:delete",
|
"resources:delete",
|
||||||
|
"admin:resources:manage",
|
||||||
"vehicles:create",
|
"vehicles:create",
|
||||||
"vehicles:read",
|
"vehicles:read",
|
||||||
"vehicles:update",
|
"vehicles:update",
|
||||||
"vehicles:delete",
|
"vehicles:delete",
|
||||||
|
"admin:vehicles:manage",
|
||||||
"structures:create",
|
"structures:create",
|
||||||
"structures:read",
|
"structures:read",
|
||||||
"structures:update",
|
"structures:update",
|
||||||
"structures:delete",
|
"structures:delete",
|
||||||
|
"admin:structures:manage",
|
||||||
"shipments:create",
|
"shipments:create",
|
||||||
"shipments:read",
|
"shipments:read",
|
||||||
"shipments:update",
|
"shipments:update",
|
||||||
"shipments:delete",
|
"shipments:delete",
|
||||||
|
"admin:shipments:manage",
|
||||||
"bank-accounts:create",
|
"bank-accounts:create",
|
||||||
"bank-accounts:read",
|
"bank-accounts:read",
|
||||||
"bank-accounts:update",
|
"bank-accounts:update",
|
||||||
"bank-accounts:delete",
|
"bank-accounts:delete",
|
||||||
|
"admin:bank-accounts:manage",
|
||||||
"bank-transactions:create",
|
"bank-transactions:create",
|
||||||
"bank-transactions:read",
|
"bank-transactions:read",
|
||||||
"bank-transactions:update",
|
"bank-transactions:update",
|
||||||
"bank-transactions:delete",
|
"bank-transactions:delete",
|
||||||
|
"admin:bank-transactions:manage",
|
||||||
"ledger-entries:create",
|
"ledger-entries:create",
|
||||||
"ledger-entries:read",
|
"ledger-entries:read",
|
||||||
"ledger-entries:update",
|
"ledger-entries:update",
|
||||||
"ledger-entries:delete",
|
"ledger-entries:delete",
|
||||||
|
"admin:ledger-entries:manage",
|
||||||
"locker-storages:create",
|
"locker-storages:create",
|
||||||
"locker-storages:read",
|
"locker-storages:read",
|
||||||
"locker-storages:update",
|
"locker-storages:update",
|
||||||
"locker-storages:delete",
|
"locker-storages:delete",
|
||||||
|
"admin:locker-storages:manage",
|
||||||
"loadouts:create",
|
"loadouts:create",
|
||||||
"loadouts:read",
|
"loadouts:read",
|
||||||
"loadouts:update",
|
"loadouts:update",
|
||||||
"loadouts:delete",
|
"loadouts:delete",
|
||||||
|
"admin:loadouts:manage",
|
||||||
"market-listings:create",
|
"market-listings:create",
|
||||||
"market-listings:read",
|
"market-listings:read",
|
||||||
"market-listings:update",
|
"market-listings:update",
|
||||||
"market-listings:delete",
|
"market-listings:delete",
|
||||||
|
"admin:market-listings:manage",
|
||||||
"market-negotiations:create",
|
"market-negotiations:create",
|
||||||
"market-negotiations:read",
|
"market-negotiations:read",
|
||||||
"market-negotiations:update",
|
"market-negotiations:update",
|
||||||
"market-negotiations:delete",
|
"market-negotiations:delete",
|
||||||
|
"admin:market-negotiations:manage",
|
||||||
"projects:create",
|
"projects:create",
|
||||||
"projects:read",
|
"projects:read",
|
||||||
"projects:update",
|
"projects:update",
|
||||||
"projects:delete",
|
"projects:delete",
|
||||||
|
"admin:projects:manage",
|
||||||
"labels:create",
|
"labels:create",
|
||||||
"labels:read",
|
"labels:read",
|
||||||
"labels:update",
|
"labels:update",
|
||||||
"labels:delete",
|
"labels:delete",
|
||||||
|
"admin:labels:manage",
|
||||||
"sprints:create",
|
"sprints:create",
|
||||||
"sprints:read",
|
"sprints:read",
|
||||||
"sprints:update",
|
"sprints:update",
|
||||||
"sprints:delete",
|
"sprints:delete",
|
||||||
|
"admin:sprints:manage",
|
||||||
"releases:create",
|
"releases:create",
|
||||||
"releases:read",
|
"releases:read",
|
||||||
"releases:update",
|
"releases:update",
|
||||||
"releases:delete",
|
"releases:delete",
|
||||||
|
"admin:releases:manage",
|
||||||
"tickets:create",
|
"tickets:create",
|
||||||
"tickets:read",
|
"tickets:read",
|
||||||
"tickets:update",
|
"tickets:update",
|
||||||
"tickets:delete",
|
"tickets:delete",
|
||||||
|
"admin:tickets:manage",
|
||||||
|
"ticket-votes:create",
|
||||||
|
"ticket-votes:read",
|
||||||
|
"ticket-votes:update",
|
||||||
|
"ticket-votes:delete",
|
||||||
|
"admin:ticket-votes:manage",
|
||||||
"game-structures:create",
|
"game-structures:create",
|
||||||
"game-structures:read",
|
"game-structures:read",
|
||||||
"game-structures:update",
|
"game-structures:update",
|
||||||
"game-structures:delete",
|
"game-structures:delete",
|
||||||
|
"admin:game-structures:manage",
|
||||||
"game-vehicles:create",
|
"game-vehicles:create",
|
||||||
"game-vehicles:read",
|
"game-vehicles:read",
|
||||||
"game-vehicles:update",
|
"game-vehicles:update",
|
||||||
"game-vehicles:delete",
|
"game-vehicles:delete",
|
||||||
|
"admin:game-vehicles:manage",
|
||||||
"game-npcs:create",
|
"game-npcs:create",
|
||||||
"game-npcs:read",
|
"game-npcs:read",
|
||||||
"game-npcs:update",
|
"game-npcs:update",
|
||||||
"game-npcs:delete",
|
"game-npcs:delete",
|
||||||
|
"admin:game-npcs:manage",
|
||||||
"game-hard-resources:create",
|
"game-hard-resources:create",
|
||||||
"game-hard-resources:read",
|
"game-hard-resources:read",
|
||||||
"game-hard-resources:update",
|
"game-hard-resources:update",
|
||||||
"game-hard-resources:delete",
|
"game-hard-resources:delete",
|
||||||
|
"admin:game-hard-resources:manage",
|
||||||
"game-event-logs:create",
|
"game-event-logs:create",
|
||||||
"game-event-logs:read",
|
"game-event-logs:read",
|
||||||
"game-event-logs:update",
|
"game-event-logs:update",
|
||||||
"game-event-logs:delete",
|
"game-event-logs:delete",
|
||||||
|
"admin:game-event-logs:manage",
|
||||||
"maps:create",
|
"maps:create",
|
||||||
"maps:read",
|
"maps:read",
|
||||||
"maps:update",
|
"maps:update",
|
||||||
"maps:delete",
|
"maps:delete",
|
||||||
|
"admin:maps:manage",
|
||||||
"narrative-events:create",
|
"narrative-events:create",
|
||||||
"narrative-events:read",
|
"narrative-events:read",
|
||||||
"narrative-events:update",
|
"narrative-events:update",
|
||||||
"narrative-events:delete",
|
"narrative-events:delete",
|
||||||
|
"admin:narrative-events:manage",
|
||||||
"mission-files:create",
|
"mission-files:create",
|
||||||
"mission-files:read",
|
"mission-files:read",
|
||||||
"mission-files:update",
|
"mission-files:update",
|
||||||
"mission-files:delete",
|
"mission-files:delete",
|
||||||
|
"admin:mission-files:manage",
|
||||||
"mod-lists:create",
|
"mod-lists:create",
|
||||||
"mod-lists:read",
|
"mod-lists:read",
|
||||||
"mod-lists:update",
|
"mod-lists:update",
|
||||||
"mod-lists:delete",
|
"mod-lists:delete",
|
||||||
|
"admin:mod-lists:manage",
|
||||||
|
"game-servers:create",
|
||||||
|
"game-servers:read",
|
||||||
|
"game-servers:update",
|
||||||
|
"game-servers:delete",
|
||||||
|
"admin:game-servers:manage",
|
||||||
|
"arma-sync-events:create",
|
||||||
|
"arma-sync-events:read",
|
||||||
|
"arma-sync-events:update",
|
||||||
|
"arma-sync-events:delete",
|
||||||
|
"admin:arma-sync-events:manage",
|
||||||
|
"arma-commands:create",
|
||||||
|
"arma-commands:read",
|
||||||
|
"arma-commands:update",
|
||||||
|
"arma-commands:delete",
|
||||||
|
"admin:arma-commands:manage",
|
||||||
"game-rules:read",
|
"game-rules:read",
|
||||||
"game-rules:update",
|
"game-rules:update",
|
||||||
"shims:read",
|
"shims:read",
|
||||||
|
|
|
||||||
|
|
@ -1259,170 +1259,245 @@ export interface Role {
|
||||||
permissions?:
|
permissions?:
|
||||||
| (
|
| (
|
||||||
| 'system:admin-access'
|
| 'system:admin-access'
|
||||||
|
| 'media:create'
|
||||||
|
| 'media:read'
|
||||||
|
| 'media:update'
|
||||||
|
| 'media:delete'
|
||||||
|
| 'admin:media:manage'
|
||||||
| 'users:create'
|
| 'users:create'
|
||||||
| 'users:read'
|
| 'users:read'
|
||||||
| 'users:update'
|
| 'users:update'
|
||||||
| 'users:delete'
|
| 'users:delete'
|
||||||
| 'users:unlock'
|
| 'users:unlock'
|
||||||
| 'users:assign-roles'
|
| 'users:assign-roles'
|
||||||
|
| 'admin:users:manage'
|
||||||
|
| 'roles:create'
|
||||||
|
| 'roles:read'
|
||||||
|
| 'roles:update'
|
||||||
|
| 'roles:delete'
|
||||||
|
| 'admin:roles:manage'
|
||||||
| 'ranks:create'
|
| 'ranks:create'
|
||||||
| 'ranks:read'
|
| 'ranks:read'
|
||||||
| 'ranks:update'
|
| 'ranks:update'
|
||||||
| 'ranks:delete'
|
| 'ranks:delete'
|
||||||
|
| 'admin:ranks:manage'
|
||||||
| 'profiles:create'
|
| 'profiles:create'
|
||||||
| 'profiles:read'
|
| 'profiles:read'
|
||||||
| 'profiles:update'
|
| 'profiles:update'
|
||||||
| 'profiles:delete'
|
| 'profiles:delete'
|
||||||
|
| 'admin:profiles:manage'
|
||||||
| 'awards:create'
|
| 'awards:create'
|
||||||
| 'awards:read'
|
| 'awards:read'
|
||||||
| 'awards:update'
|
| 'awards:update'
|
||||||
| 'awards:delete'
|
| 'awards:delete'
|
||||||
|
| 'admin:awards:manage'
|
||||||
| 'qualifications:create'
|
| 'qualifications:create'
|
||||||
| 'qualifications:read'
|
| 'qualifications:read'
|
||||||
| 'qualifications:update'
|
| 'qualifications:update'
|
||||||
| 'qualifications:delete'
|
| 'qualifications:delete'
|
||||||
|
| 'admin:qualifications:manage'
|
||||||
| 'assignments:create'
|
| 'assignments:create'
|
||||||
| 'assignments:read'
|
| 'assignments:read'
|
||||||
| 'assignments:update'
|
| 'assignments:update'
|
||||||
| 'assignments:delete'
|
| 'assignments:delete'
|
||||||
|
| 'admin:assignments:manage'
|
||||||
|
| 'assignment-transfers:create'
|
||||||
|
| 'assignment-transfers:read'
|
||||||
|
| 'assignment-transfers:update'
|
||||||
|
| 'assignment-transfers:delete'
|
||||||
|
| 'admin:assignment-transfers:manage'
|
||||||
| 'experience:create'
|
| 'experience:create'
|
||||||
| 'experience:read'
|
| 'experience:read'
|
||||||
| 'experience:update'
|
| 'experience:update'
|
||||||
| 'experience:delete'
|
| 'experience:delete'
|
||||||
|
| 'admin:experience:manage'
|
||||||
| 'user-notifications:create'
|
| 'user-notifications:create'
|
||||||
| 'user-notifications:read'
|
| 'user-notifications:read'
|
||||||
| 'user-notifications:update'
|
| 'user-notifications:update'
|
||||||
| 'user-notifications:delete'
|
| 'user-notifications:delete'
|
||||||
|
| 'admin:user-notifications:manage'
|
||||||
| 'evaluations:create'
|
| 'evaluations:create'
|
||||||
| 'evaluations:read'
|
| 'evaluations:read'
|
||||||
| 'evaluations:update'
|
| 'evaluations:update'
|
||||||
| 'evaluations:delete'
|
| 'evaluations:delete'
|
||||||
|
| 'admin:evaluations:manage'
|
||||||
| 'missions:create'
|
| 'missions:create'
|
||||||
| 'missions:read'
|
| 'missions:read'
|
||||||
| 'missions:update'
|
| 'missions:update'
|
||||||
| 'missions:delete'
|
| 'missions:delete'
|
||||||
| 'missions:read-sensitive'
|
| 'missions:read-sensitive'
|
||||||
|
| 'admin:missions:manage'
|
||||||
| 'mission-attendances:create'
|
| 'mission-attendances:create'
|
||||||
| 'mission-attendances:read'
|
| 'mission-attendances:read'
|
||||||
| 'mission-attendances:update'
|
| 'mission-attendances:update'
|
||||||
| 'mission-attendances:delete'
|
| 'mission-attendances:delete'
|
||||||
|
| 'admin:mission-attendances:manage'
|
||||||
| 'campaigns:create'
|
| 'campaigns:create'
|
||||||
| 'campaigns:read'
|
| 'campaigns:read'
|
||||||
| 'campaigns:update'
|
| 'campaigns:update'
|
||||||
| 'campaigns:delete'
|
| 'campaigns:delete'
|
||||||
|
| 'admin:campaigns:manage'
|
||||||
| 'campaigns:manage'
|
| 'campaigns:manage'
|
||||||
| 'factions:create'
|
| 'factions:create'
|
||||||
| 'factions:read'
|
| 'factions:read'
|
||||||
| 'factions:update'
|
| 'factions:update'
|
||||||
| 'factions:delete'
|
| 'factions:delete'
|
||||||
|
| 'admin:factions:manage'
|
||||||
| 'technologies:create'
|
| 'technologies:create'
|
||||||
| 'technologies:read'
|
| 'technologies:read'
|
||||||
| 'technologies:update'
|
| 'technologies:update'
|
||||||
| 'technologies:delete'
|
| 'technologies:delete'
|
||||||
|
| 'admin:technologies:manage'
|
||||||
| 'assets:create'
|
| 'assets:create'
|
||||||
| 'assets:read'
|
| 'assets:read'
|
||||||
| 'assets:update'
|
| 'assets:update'
|
||||||
| 'assets:delete'
|
| 'assets:delete'
|
||||||
|
| 'admin:assets:manage'
|
||||||
| 'resources:create'
|
| 'resources:create'
|
||||||
| 'resources:read'
|
| 'resources:read'
|
||||||
| 'resources:update'
|
| 'resources:update'
|
||||||
| 'resources:delete'
|
| 'resources:delete'
|
||||||
|
| 'admin:resources:manage'
|
||||||
| 'vehicles:create'
|
| 'vehicles:create'
|
||||||
| 'vehicles:read'
|
| 'vehicles:read'
|
||||||
| 'vehicles:update'
|
| 'vehicles:update'
|
||||||
| 'vehicles:delete'
|
| 'vehicles:delete'
|
||||||
|
| 'admin:vehicles:manage'
|
||||||
| 'structures:create'
|
| 'structures:create'
|
||||||
| 'structures:read'
|
| 'structures:read'
|
||||||
| 'structures:update'
|
| 'structures:update'
|
||||||
| 'structures:delete'
|
| 'structures:delete'
|
||||||
|
| 'admin:structures:manage'
|
||||||
| 'shipments:create'
|
| 'shipments:create'
|
||||||
| 'shipments:read'
|
| 'shipments:read'
|
||||||
| 'shipments:update'
|
| 'shipments:update'
|
||||||
| 'shipments:delete'
|
| 'shipments:delete'
|
||||||
|
| 'admin:shipments:manage'
|
||||||
| 'bank-accounts:create'
|
| 'bank-accounts:create'
|
||||||
| 'bank-accounts:read'
|
| 'bank-accounts:read'
|
||||||
| 'bank-accounts:update'
|
| 'bank-accounts:update'
|
||||||
| 'bank-accounts:delete'
|
| 'bank-accounts:delete'
|
||||||
|
| 'admin:bank-accounts:manage'
|
||||||
| 'bank-transactions:create'
|
| 'bank-transactions:create'
|
||||||
| 'bank-transactions:read'
|
| 'bank-transactions:read'
|
||||||
| 'bank-transactions:update'
|
| 'bank-transactions:update'
|
||||||
| 'bank-transactions:delete'
|
| 'bank-transactions:delete'
|
||||||
|
| 'admin:bank-transactions:manage'
|
||||||
| 'ledger-entries:create'
|
| 'ledger-entries:create'
|
||||||
| 'ledger-entries:read'
|
| 'ledger-entries:read'
|
||||||
| 'ledger-entries:update'
|
| 'ledger-entries:update'
|
||||||
| 'ledger-entries:delete'
|
| 'ledger-entries:delete'
|
||||||
|
| 'admin:ledger-entries:manage'
|
||||||
| 'locker-storages:create'
|
| 'locker-storages:create'
|
||||||
| 'locker-storages:read'
|
| 'locker-storages:read'
|
||||||
| 'locker-storages:update'
|
| 'locker-storages:update'
|
||||||
| 'locker-storages:delete'
|
| 'locker-storages:delete'
|
||||||
|
| 'admin:locker-storages:manage'
|
||||||
| 'loadouts:create'
|
| 'loadouts:create'
|
||||||
| 'loadouts:read'
|
| 'loadouts:read'
|
||||||
| 'loadouts:update'
|
| 'loadouts:update'
|
||||||
| 'loadouts:delete'
|
| 'loadouts:delete'
|
||||||
|
| 'admin:loadouts:manage'
|
||||||
| 'market-listings:create'
|
| 'market-listings:create'
|
||||||
| 'market-listings:read'
|
| 'market-listings:read'
|
||||||
| 'market-listings:update'
|
| 'market-listings:update'
|
||||||
| 'market-listings:delete'
|
| 'market-listings:delete'
|
||||||
|
| 'admin:market-listings:manage'
|
||||||
| 'market-negotiations:create'
|
| 'market-negotiations:create'
|
||||||
| 'market-negotiations:read'
|
| 'market-negotiations:read'
|
||||||
| 'market-negotiations:update'
|
| 'market-negotiations:update'
|
||||||
| 'market-negotiations:delete'
|
| 'market-negotiations:delete'
|
||||||
|
| 'admin:market-negotiations:manage'
|
||||||
| 'projects:create'
|
| 'projects:create'
|
||||||
| 'projects:read'
|
| 'projects:read'
|
||||||
| 'projects:update'
|
| 'projects:update'
|
||||||
| 'projects:delete'
|
| 'projects:delete'
|
||||||
|
| 'admin:projects:manage'
|
||||||
| 'labels:create'
|
| 'labels:create'
|
||||||
| 'labels:read'
|
| 'labels:read'
|
||||||
| 'labels:update'
|
| 'labels:update'
|
||||||
| 'labels:delete'
|
| 'labels:delete'
|
||||||
|
| 'admin:labels:manage'
|
||||||
| 'sprints:create'
|
| 'sprints:create'
|
||||||
| 'sprints:read'
|
| 'sprints:read'
|
||||||
| 'sprints:update'
|
| 'sprints:update'
|
||||||
| 'sprints:delete'
|
| 'sprints:delete'
|
||||||
|
| 'admin:sprints:manage'
|
||||||
| 'releases:create'
|
| 'releases:create'
|
||||||
| 'releases:read'
|
| 'releases:read'
|
||||||
| 'releases:update'
|
| 'releases:update'
|
||||||
| 'releases:delete'
|
| 'releases:delete'
|
||||||
|
| 'admin:releases:manage'
|
||||||
| 'tickets:create'
|
| 'tickets:create'
|
||||||
| 'tickets:read'
|
| 'tickets:read'
|
||||||
| 'tickets:update'
|
| 'tickets:update'
|
||||||
| 'tickets:delete'
|
| 'tickets:delete'
|
||||||
|
| 'admin:tickets:manage'
|
||||||
|
| 'ticket-votes:create'
|
||||||
|
| 'ticket-votes:read'
|
||||||
|
| 'ticket-votes:update'
|
||||||
|
| 'ticket-votes:delete'
|
||||||
|
| 'admin:ticket-votes:manage'
|
||||||
| 'game-structures:create'
|
| 'game-structures:create'
|
||||||
| 'game-structures:read'
|
| 'game-structures:read'
|
||||||
| 'game-structures:update'
|
| 'game-structures:update'
|
||||||
| 'game-structures:delete'
|
| 'game-structures:delete'
|
||||||
|
| 'admin:game-structures:manage'
|
||||||
| 'game-vehicles:create'
|
| 'game-vehicles:create'
|
||||||
| 'game-vehicles:read'
|
| 'game-vehicles:read'
|
||||||
| 'game-vehicles:update'
|
| 'game-vehicles:update'
|
||||||
| 'game-vehicles:delete'
|
| 'game-vehicles:delete'
|
||||||
|
| 'admin:game-vehicles:manage'
|
||||||
| 'game-npcs:create'
|
| 'game-npcs:create'
|
||||||
| 'game-npcs:read'
|
| 'game-npcs:read'
|
||||||
| 'game-npcs:update'
|
| 'game-npcs:update'
|
||||||
| 'game-npcs:delete'
|
| 'game-npcs:delete'
|
||||||
|
| 'admin:game-npcs:manage'
|
||||||
| 'game-hard-resources:create'
|
| 'game-hard-resources:create'
|
||||||
| 'game-hard-resources:read'
|
| 'game-hard-resources:read'
|
||||||
| 'game-hard-resources:update'
|
| 'game-hard-resources:update'
|
||||||
| 'game-hard-resources:delete'
|
| 'game-hard-resources:delete'
|
||||||
|
| 'admin:game-hard-resources:manage'
|
||||||
| 'game-event-logs:create'
|
| 'game-event-logs:create'
|
||||||
| 'game-event-logs:read'
|
| 'game-event-logs:read'
|
||||||
| 'game-event-logs:update'
|
| 'game-event-logs:update'
|
||||||
| 'game-event-logs:delete'
|
| 'game-event-logs:delete'
|
||||||
|
| 'admin:game-event-logs:manage'
|
||||||
| 'maps:create'
|
| 'maps:create'
|
||||||
| 'maps:read'
|
| 'maps:read'
|
||||||
| 'maps:update'
|
| 'maps:update'
|
||||||
| 'maps:delete'
|
| 'maps:delete'
|
||||||
|
| 'admin:maps:manage'
|
||||||
| 'narrative-events:create'
|
| 'narrative-events:create'
|
||||||
| 'narrative-events:read'
|
| 'narrative-events:read'
|
||||||
| 'narrative-events:update'
|
| 'narrative-events:update'
|
||||||
| 'narrative-events:delete'
|
| 'narrative-events:delete'
|
||||||
|
| 'admin:narrative-events:manage'
|
||||||
| 'mission-files:create'
|
| 'mission-files:create'
|
||||||
| 'mission-files:read'
|
| 'mission-files:read'
|
||||||
| 'mission-files:update'
|
| 'mission-files:update'
|
||||||
| 'mission-files:delete'
|
| 'mission-files:delete'
|
||||||
|
| 'admin:mission-files:manage'
|
||||||
| 'mod-lists:create'
|
| 'mod-lists:create'
|
||||||
| 'mod-lists:read'
|
| 'mod-lists:read'
|
||||||
| 'mod-lists:update'
|
| 'mod-lists:update'
|
||||||
| 'mod-lists:delete'
|
| 'mod-lists:delete'
|
||||||
|
| 'admin:mod-lists:manage'
|
||||||
|
| 'game-servers:create'
|
||||||
|
| 'game-servers:read'
|
||||||
|
| 'game-servers:update'
|
||||||
|
| 'game-servers:delete'
|
||||||
|
| 'admin:game-servers:manage'
|
||||||
|
| 'arma-sync-events:create'
|
||||||
|
| 'arma-sync-events:read'
|
||||||
|
| 'arma-sync-events:update'
|
||||||
|
| 'arma-sync-events:delete'
|
||||||
|
| 'admin:arma-sync-events:manage'
|
||||||
|
| 'arma-commands:create'
|
||||||
|
| 'arma-commands:read'
|
||||||
|
| 'arma-commands:update'
|
||||||
|
| 'arma-commands:delete'
|
||||||
|
| 'admin:arma-commands:manage'
|
||||||
| 'game-rules:read'
|
| 'game-rules:read'
|
||||||
| 'game-rules:update'
|
| 'game-rules:update'
|
||||||
| 'shims:read'
|
| 'shims:read'
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ import { migrations } from "./migrations";
|
||||||
import { nodemailerAdapter } from "@payloadcms/email-nodemailer";
|
import { nodemailerAdapter } from "@payloadcms/email-nodemailer";
|
||||||
import nodemailer from "nodemailer";
|
import nodemailer from "nodemailer";
|
||||||
import { Shims } from "@/collections/Shims";
|
import { Shims } from "@/collections/Shims";
|
||||||
|
import { requireAdminPageAccess } from "@/utils/access-control/hasPermission";
|
||||||
import { payloadAiPlugin, PayloadAiPluginLexicalEditorFeature } from "@ai-stack/payloadcms";
|
import { payloadAiPlugin, PayloadAiPluginLexicalEditorFeature } from "@ai-stack/payloadcms";
|
||||||
import { mcpPlugin } from "@payloadcms/plugin-mcp";
|
import { mcpPlugin } from "@payloadcms/plugin-mcp";
|
||||||
import { createOpenAI } from "@ai-sdk/openai";
|
import { createOpenAI } from "@ai-sdk/openai";
|
||||||
|
|
@ -139,6 +140,89 @@ const dirname = path.dirname(filename);
|
||||||
const META_TITLE = "Polaris Task Force";
|
const META_TITLE = "Polaris Task Force";
|
||||||
const META_DESCRIPTION = "A gamified platform for Polaris Task Force, an Arma 3 unit.";
|
const META_DESCRIPTION = "A gamified platform for Polaris Task Force, an Arma 3 unit.";
|
||||||
|
|
||||||
|
const collections = [
|
||||||
|
Media,
|
||||||
|
|
||||||
|
// Game
|
||||||
|
GameHardResources,
|
||||||
|
GameStructures,
|
||||||
|
GameVehicles,
|
||||||
|
GameNpcs,
|
||||||
|
GameEventLogs,
|
||||||
|
|
||||||
|
// Users
|
||||||
|
Users,
|
||||||
|
Roles,
|
||||||
|
Ranks,
|
||||||
|
Profiles,
|
||||||
|
Awards,
|
||||||
|
Qualifications,
|
||||||
|
Assignments,
|
||||||
|
AssignmentTransfers,
|
||||||
|
Experience,
|
||||||
|
UserNotifications,
|
||||||
|
Evaluations,
|
||||||
|
|
||||||
|
// Intel
|
||||||
|
Missions,
|
||||||
|
MissionAttendances,
|
||||||
|
Campaigns,
|
||||||
|
Factions,
|
||||||
|
Technologies,
|
||||||
|
|
||||||
|
// Logistics
|
||||||
|
Assets,
|
||||||
|
Resources,
|
||||||
|
Vehicles,
|
||||||
|
Structures,
|
||||||
|
Shipments,
|
||||||
|
|
||||||
|
// Banking
|
||||||
|
BankAccounts,
|
||||||
|
BankTransactions,
|
||||||
|
LedgerEntries,
|
||||||
|
|
||||||
|
// Locker
|
||||||
|
LockerStorages,
|
||||||
|
Loadouts,
|
||||||
|
|
||||||
|
// Market
|
||||||
|
MarketListings,
|
||||||
|
MarketNegotiations,
|
||||||
|
|
||||||
|
// Helpdesk
|
||||||
|
Tickets,
|
||||||
|
TicketVotes,
|
||||||
|
|
||||||
|
// Projects
|
||||||
|
Projects,
|
||||||
|
Sprints,
|
||||||
|
Releases,
|
||||||
|
Labels,
|
||||||
|
|
||||||
|
// World
|
||||||
|
Maps,
|
||||||
|
NarrativeEvents,
|
||||||
|
|
||||||
|
// Server
|
||||||
|
MissionFiles,
|
||||||
|
ModLists,
|
||||||
|
GameServers,
|
||||||
|
ArmaSyncEvents,
|
||||||
|
ArmaCommands,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Scoped admin pages: a user needs BOTH `<slug>:read` AND `admin:<slug>:manage`
|
||||||
|
// to see/interact with a collection in the Payload admin panel. Non-admin
|
||||||
|
// (REST/API) reads keep the collection's original access behavior.
|
||||||
|
const scopedCollections = collections.map((collection) => ({
|
||||||
|
...collection,
|
||||||
|
access: {
|
||||||
|
...collection.access,
|
||||||
|
read: requireAdminPageAccess(collection.slug, collection.access?.read),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
export default buildConfig({
|
export default buildConfig({
|
||||||
// serverURL: "http://localhost:3000/",
|
// serverURL: "http://localhost:3000/",
|
||||||
admin: {
|
admin: {
|
||||||
|
|
@ -186,77 +270,7 @@ export default buildConfig({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
globals: [GameRules, Shims],
|
globals: [GameRules, Shims],
|
||||||
collections: [
|
collections: scopedCollections,
|
||||||
Media,
|
|
||||||
|
|
||||||
// Game
|
|
||||||
GameHardResources,
|
|
||||||
GameStructures,
|
|
||||||
GameVehicles,
|
|
||||||
GameNpcs,
|
|
||||||
GameEventLogs,
|
|
||||||
|
|
||||||
// Users
|
|
||||||
Users,
|
|
||||||
Roles,
|
|
||||||
Ranks,
|
|
||||||
Profiles,
|
|
||||||
Awards,
|
|
||||||
Qualifications,
|
|
||||||
Assignments,
|
|
||||||
AssignmentTransfers,
|
|
||||||
Experience,
|
|
||||||
UserNotifications,
|
|
||||||
Evaluations,
|
|
||||||
|
|
||||||
// Intel
|
|
||||||
Missions,
|
|
||||||
MissionAttendances,
|
|
||||||
Campaigns,
|
|
||||||
Factions,
|
|
||||||
Technologies,
|
|
||||||
|
|
||||||
// Logistics
|
|
||||||
Assets,
|
|
||||||
Resources,
|
|
||||||
Vehicles,
|
|
||||||
Structures,
|
|
||||||
Shipments,
|
|
||||||
|
|
||||||
// Banking
|
|
||||||
BankAccounts,
|
|
||||||
BankTransactions,
|
|
||||||
LedgerEntries,
|
|
||||||
|
|
||||||
// Locker
|
|
||||||
LockerStorages,
|
|
||||||
Loadouts,
|
|
||||||
|
|
||||||
// Market
|
|
||||||
MarketListings,
|
|
||||||
MarketNegotiations,
|
|
||||||
|
|
||||||
// Helpdesk
|
|
||||||
Tickets,
|
|
||||||
TicketVotes,
|
|
||||||
|
|
||||||
// Projects
|
|
||||||
Projects,
|
|
||||||
Sprints,
|
|
||||||
Releases,
|
|
||||||
Labels,
|
|
||||||
|
|
||||||
// World
|
|
||||||
Maps,
|
|
||||||
NarrativeEvents,
|
|
||||||
|
|
||||||
// Server
|
|
||||||
MissionFiles,
|
|
||||||
ModLists,
|
|
||||||
GameServers,
|
|
||||||
ArmaSyncEvents,
|
|
||||||
ArmaCommands,
|
|
||||||
],
|
|
||||||
editor: lexicalEditor({
|
editor: lexicalEditor({
|
||||||
features: ({ rootFeatures }) => {
|
features: ({ rootFeatures }) => {
|
||||||
return [HeadingFeature(), PayloadAiPluginLexicalEditorFeature()];
|
return [HeadingFeature(), PayloadAiPluginLexicalEditorFeature()];
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,18 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
permissions: [{ value: "system:admin-access", label: "Access Admin Panel" }],
|
permissions: [{ value: "system:admin-access", label: "Access Admin Panel" }],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ---- Media ---------------------------------------------------------------
|
||||||
|
{
|
||||||
|
group: "Media",
|
||||||
|
permissions: [
|
||||||
|
{ value: "media:create", label: "Create" },
|
||||||
|
{ value: "media:read", label: "Read" },
|
||||||
|
{ value: "media:update", label: "Update" },
|
||||||
|
{ value: "media:delete", label: "Delete" },
|
||||||
|
{ value: "admin:media:manage", label: "Manage Admin Page" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
// ---- Users ---------------------------------------------------------------
|
// ---- Users ---------------------------------------------------------------
|
||||||
{
|
{
|
||||||
group: "Users",
|
group: "Users",
|
||||||
|
|
@ -47,6 +59,17 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "users:delete", label: "Delete Users" },
|
{ value: "users:delete", label: "Delete Users" },
|
||||||
{ value: "users:unlock", label: "Unlock User Accounts" },
|
{ value: "users:unlock", label: "Unlock User Accounts" },
|
||||||
{ value: "users:assign-roles", label: "Assign Roles to Users" },
|
{ value: "users:assign-roles", label: "Assign Roles to Users" },
|
||||||
|
{ value: "admin:users:manage", label: "Manage Admin Page" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: "Roles",
|
||||||
|
permissions: [
|
||||||
|
{ value: "roles:create", label: "Create" },
|
||||||
|
{ value: "roles:read", label: "Read" },
|
||||||
|
{ value: "roles:update", label: "Update" },
|
||||||
|
{ value: "roles:delete", label: "Delete" },
|
||||||
|
{ value: "admin:roles:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -56,6 +79,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "ranks:read", label: "Read" },
|
{ value: "ranks:read", label: "Read" },
|
||||||
{ value: "ranks:update", label: "Update" },
|
{ value: "ranks:update", label: "Update" },
|
||||||
{ value: "ranks:delete", label: "Delete" },
|
{ value: "ranks:delete", label: "Delete" },
|
||||||
|
{ value: "admin:ranks:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -65,6 +89,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "profiles:read", label: "Read" },
|
{ value: "profiles:read", label: "Read" },
|
||||||
{ value: "profiles:update", label: "Update" },
|
{ value: "profiles:update", label: "Update" },
|
||||||
{ value: "profiles:delete", label: "Delete" },
|
{ value: "profiles:delete", label: "Delete" },
|
||||||
|
{ value: "admin:profiles:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -74,6 +99,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "awards:read", label: "Read" },
|
{ value: "awards:read", label: "Read" },
|
||||||
{ value: "awards:update", label: "Update" },
|
{ value: "awards:update", label: "Update" },
|
||||||
{ value: "awards:delete", label: "Delete" },
|
{ value: "awards:delete", label: "Delete" },
|
||||||
|
{ value: "admin:awards:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -83,6 +109,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "qualifications:read", label: "Read" },
|
{ value: "qualifications:read", label: "Read" },
|
||||||
{ value: "qualifications:update", label: "Update" },
|
{ value: "qualifications:update", label: "Update" },
|
||||||
{ value: "qualifications:delete", label: "Delete" },
|
{ value: "qualifications:delete", label: "Delete" },
|
||||||
|
{ value: "admin:qualifications:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -92,6 +119,17 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "assignments:read", label: "Read" },
|
{ value: "assignments:read", label: "Read" },
|
||||||
{ value: "assignments:update", label: "Update" },
|
{ value: "assignments:update", label: "Update" },
|
||||||
{ value: "assignments:delete", label: "Delete" },
|
{ value: "assignments:delete", label: "Delete" },
|
||||||
|
{ value: "admin:assignments:manage", label: "Manage Admin Page" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: "Assignment Transfers",
|
||||||
|
permissions: [
|
||||||
|
{ value: "assignment-transfers:create", label: "Create" },
|
||||||
|
{ value: "assignment-transfers:read", label: "Read" },
|
||||||
|
{ value: "assignment-transfers:update", label: "Update" },
|
||||||
|
{ value: "assignment-transfers:delete", label: "Delete" },
|
||||||
|
{ value: "admin:assignment-transfers:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -101,6 +139,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "experience:read", label: "Read" },
|
{ value: "experience:read", label: "Read" },
|
||||||
{ value: "experience:update", label: "Update" },
|
{ value: "experience:update", label: "Update" },
|
||||||
{ value: "experience:delete", label: "Delete" },
|
{ value: "experience:delete", label: "Delete" },
|
||||||
|
{ value: "admin:experience:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -110,6 +149,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "user-notifications:read", label: "Read" },
|
{ value: "user-notifications:read", label: "Read" },
|
||||||
{ value: "user-notifications:update", label: "Update" },
|
{ value: "user-notifications:update", label: "Update" },
|
||||||
{ value: "user-notifications:delete", label: "Delete" },
|
{ value: "user-notifications:delete", label: "Delete" },
|
||||||
|
{ value: "admin:user-notifications:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -119,6 +159,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "evaluations:read", label: "Read" },
|
{ value: "evaluations:read", label: "Read" },
|
||||||
{ value: "evaluations:update", label: "Update" },
|
{ value: "evaluations:update", label: "Update" },
|
||||||
{ value: "evaluations:delete", label: "Delete" },
|
{ value: "evaluations:delete", label: "Delete" },
|
||||||
|
{ value: "admin:evaluations:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -131,6 +172,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "missions:update", label: "Update" },
|
{ value: "missions:update", label: "Update" },
|
||||||
{ value: "missions:delete", label: "Delete" },
|
{ value: "missions:delete", label: "Delete" },
|
||||||
{ value: "missions:read-sensitive", label: "Read Sensitive Fields (server passwords)" },
|
{ value: "missions:read-sensitive", label: "Read Sensitive Fields (server passwords)" },
|
||||||
|
{ value: "admin:missions:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -140,6 +182,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "mission-attendances:read", label: "Read" },
|
{ value: "mission-attendances:read", label: "Read" },
|
||||||
{ value: "mission-attendances:update", label: "Update" },
|
{ value: "mission-attendances:update", label: "Update" },
|
||||||
{ value: "mission-attendances:delete", label: "Delete" },
|
{ value: "mission-attendances:delete", label: "Delete" },
|
||||||
|
{ value: "admin:mission-attendances:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -149,6 +192,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "campaigns:read", label: "Read" },
|
{ value: "campaigns:read", label: "Read" },
|
||||||
{ value: "campaigns:update", label: "Update" },
|
{ value: "campaigns:update", label: "Update" },
|
||||||
{ value: "campaigns:delete", label: "Delete" },
|
{ value: "campaigns:delete", label: "Delete" },
|
||||||
|
{ value: "admin:campaigns:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -162,6 +206,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "factions:read", label: "Read" },
|
{ value: "factions:read", label: "Read" },
|
||||||
{ value: "factions:update", label: "Update" },
|
{ value: "factions:update", label: "Update" },
|
||||||
{ value: "factions:delete", label: "Delete" },
|
{ value: "factions:delete", label: "Delete" },
|
||||||
|
{ value: "admin:factions:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -171,6 +216,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "technologies:read", label: "Read" },
|
{ value: "technologies:read", label: "Read" },
|
||||||
{ value: "technologies:update", label: "Update" },
|
{ value: "technologies:update", label: "Update" },
|
||||||
{ value: "technologies:delete", label: "Delete" },
|
{ value: "technologies:delete", label: "Delete" },
|
||||||
|
{ value: "admin:technologies:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -182,6 +228,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "assets:read", label: "Read" },
|
{ value: "assets:read", label: "Read" },
|
||||||
{ value: "assets:update", label: "Update" },
|
{ value: "assets:update", label: "Update" },
|
||||||
{ value: "assets:delete", label: "Delete" },
|
{ value: "assets:delete", label: "Delete" },
|
||||||
|
{ value: "admin:assets:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -191,6 +238,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "resources:read", label: "Read" },
|
{ value: "resources:read", label: "Read" },
|
||||||
{ value: "resources:update", label: "Update" },
|
{ value: "resources:update", label: "Update" },
|
||||||
{ value: "resources:delete", label: "Delete" },
|
{ value: "resources:delete", label: "Delete" },
|
||||||
|
{ value: "admin:resources:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -200,6 +248,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "vehicles:read", label: "Read" },
|
{ value: "vehicles:read", label: "Read" },
|
||||||
{ value: "vehicles:update", label: "Update" },
|
{ value: "vehicles:update", label: "Update" },
|
||||||
{ value: "vehicles:delete", label: "Delete" },
|
{ value: "vehicles:delete", label: "Delete" },
|
||||||
|
{ value: "admin:vehicles:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -209,6 +258,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "structures:read", label: "Read" },
|
{ value: "structures:read", label: "Read" },
|
||||||
{ value: "structures:update", label: "Update" },
|
{ value: "structures:update", label: "Update" },
|
||||||
{ value: "structures:delete", label: "Delete" },
|
{ value: "structures:delete", label: "Delete" },
|
||||||
|
{ value: "admin:structures:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -218,6 +268,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "shipments:read", label: "Read" },
|
{ value: "shipments:read", label: "Read" },
|
||||||
{ value: "shipments:update", label: "Update" },
|
{ value: "shipments:update", label: "Update" },
|
||||||
{ value: "shipments:delete", label: "Delete" },
|
{ value: "shipments:delete", label: "Delete" },
|
||||||
|
{ value: "admin:shipments:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -229,6 +280,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "bank-accounts:read", label: "Read" },
|
{ value: "bank-accounts:read", label: "Read" },
|
||||||
{ value: "bank-accounts:update", label: "Update" },
|
{ value: "bank-accounts:update", label: "Update" },
|
||||||
{ value: "bank-accounts:delete", label: "Delete" },
|
{ value: "bank-accounts:delete", label: "Delete" },
|
||||||
|
{ value: "admin:bank-accounts:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -238,6 +290,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "bank-transactions:read", label: "Read" },
|
{ value: "bank-transactions:read", label: "Read" },
|
||||||
{ value: "bank-transactions:update", label: "Update" },
|
{ value: "bank-transactions:update", label: "Update" },
|
||||||
{ value: "bank-transactions:delete", label: "Delete" },
|
{ value: "bank-transactions:delete", label: "Delete" },
|
||||||
|
{ value: "admin:bank-transactions:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -247,6 +300,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "ledger-entries:read", label: "Read" },
|
{ value: "ledger-entries:read", label: "Read" },
|
||||||
{ value: "ledger-entries:update", label: "Update" },
|
{ value: "ledger-entries:update", label: "Update" },
|
||||||
{ value: "ledger-entries:delete", label: "Delete" },
|
{ value: "ledger-entries:delete", label: "Delete" },
|
||||||
|
{ value: "admin:ledger-entries:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -258,6 +312,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "locker-storages:read", label: "Read" },
|
{ value: "locker-storages:read", label: "Read" },
|
||||||
{ value: "locker-storages:update", label: "Update" },
|
{ value: "locker-storages:update", label: "Update" },
|
||||||
{ value: "locker-storages:delete", label: "Delete" },
|
{ value: "locker-storages:delete", label: "Delete" },
|
||||||
|
{ value: "admin:locker-storages:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -267,6 +322,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "loadouts:read", label: "Read" },
|
{ value: "loadouts:read", label: "Read" },
|
||||||
{ value: "loadouts:update", label: "Update" },
|
{ value: "loadouts:update", label: "Update" },
|
||||||
{ value: "loadouts:delete", label: "Delete" },
|
{ value: "loadouts:delete", label: "Delete" },
|
||||||
|
{ value: "admin:loadouts:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -278,6 +334,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "market-listings:read", label: "Read" },
|
{ value: "market-listings:read", label: "Read" },
|
||||||
{ value: "market-listings:update", label: "Update" },
|
{ value: "market-listings:update", label: "Update" },
|
||||||
{ value: "market-listings:delete", label: "Delete" },
|
{ value: "market-listings:delete", label: "Delete" },
|
||||||
|
{ value: "admin:market-listings:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -287,6 +344,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "market-negotiations:read", label: "Read" },
|
{ value: "market-negotiations:read", label: "Read" },
|
||||||
{ value: "market-negotiations:update", label: "Update" },
|
{ value: "market-negotiations:update", label: "Update" },
|
||||||
{ value: "market-negotiations:delete", label: "Delete" },
|
{ value: "market-negotiations:delete", label: "Delete" },
|
||||||
|
{ value: "admin:market-negotiations:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -298,6 +356,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "projects:read", label: "Read" },
|
{ value: "projects:read", label: "Read" },
|
||||||
{ value: "projects:update", label: "Update" },
|
{ value: "projects:update", label: "Update" },
|
||||||
{ value: "projects:delete", label: "Delete" },
|
{ value: "projects:delete", label: "Delete" },
|
||||||
|
{ value: "admin:projects:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -307,6 +366,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "labels:read", label: "Read" },
|
{ value: "labels:read", label: "Read" },
|
||||||
{ value: "labels:update", label: "Update" },
|
{ value: "labels:update", label: "Update" },
|
||||||
{ value: "labels:delete", label: "Delete" },
|
{ value: "labels:delete", label: "Delete" },
|
||||||
|
{ value: "admin:labels:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -316,6 +376,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "sprints:read", label: "Read" },
|
{ value: "sprints:read", label: "Read" },
|
||||||
{ value: "sprints:update", label: "Update" },
|
{ value: "sprints:update", label: "Update" },
|
||||||
{ value: "sprints:delete", label: "Delete" },
|
{ value: "sprints:delete", label: "Delete" },
|
||||||
|
{ value: "admin:sprints:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -325,6 +386,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "releases:read", label: "Read" },
|
{ value: "releases:read", label: "Read" },
|
||||||
{ value: "releases:update", label: "Update" },
|
{ value: "releases:update", label: "Update" },
|
||||||
{ value: "releases:delete", label: "Delete" },
|
{ value: "releases:delete", label: "Delete" },
|
||||||
|
{ value: "admin:releases:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -334,6 +396,17 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "tickets:read", label: "Read" },
|
{ value: "tickets:read", label: "Read" },
|
||||||
{ value: "tickets:update", label: "Update" },
|
{ value: "tickets:update", label: "Update" },
|
||||||
{ value: "tickets:delete", label: "Delete" },
|
{ value: "tickets:delete", label: "Delete" },
|
||||||
|
{ value: "admin:tickets:manage", label: "Manage Admin Page" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: "Ticket Votes",
|
||||||
|
permissions: [
|
||||||
|
{ value: "ticket-votes:create", label: "Create" },
|
||||||
|
{ value: "ticket-votes:read", label: "Read" },
|
||||||
|
{ value: "ticket-votes:update", label: "Update" },
|
||||||
|
{ value: "ticket-votes:delete", label: "Delete" },
|
||||||
|
{ value: "admin:ticket-votes:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -345,6 +418,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "game-structures:read", label: "Read" },
|
{ value: "game-structures:read", label: "Read" },
|
||||||
{ value: "game-structures:update", label: "Update" },
|
{ value: "game-structures:update", label: "Update" },
|
||||||
{ value: "game-structures:delete", label: "Delete" },
|
{ value: "game-structures:delete", label: "Delete" },
|
||||||
|
{ value: "admin:game-structures:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -354,6 +428,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "game-vehicles:read", label: "Read" },
|
{ value: "game-vehicles:read", label: "Read" },
|
||||||
{ value: "game-vehicles:update", label: "Update" },
|
{ value: "game-vehicles:update", label: "Update" },
|
||||||
{ value: "game-vehicles:delete", label: "Delete" },
|
{ value: "game-vehicles:delete", label: "Delete" },
|
||||||
|
{ value: "admin:game-vehicles:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -363,6 +438,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "game-npcs:read", label: "Read" },
|
{ value: "game-npcs:read", label: "Read" },
|
||||||
{ value: "game-npcs:update", label: "Update" },
|
{ value: "game-npcs:update", label: "Update" },
|
||||||
{ value: "game-npcs:delete", label: "Delete" },
|
{ value: "game-npcs:delete", label: "Delete" },
|
||||||
|
{ value: "admin:game-npcs:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -372,6 +448,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "game-hard-resources:read", label: "Read" },
|
{ value: "game-hard-resources:read", label: "Read" },
|
||||||
{ value: "game-hard-resources:update", label: "Update" },
|
{ value: "game-hard-resources:update", label: "Update" },
|
||||||
{ value: "game-hard-resources:delete", label: "Delete" },
|
{ value: "game-hard-resources:delete", label: "Delete" },
|
||||||
|
{ value: "admin:game-hard-resources:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -381,6 +458,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "game-event-logs:read", label: "Read" },
|
{ value: "game-event-logs:read", label: "Read" },
|
||||||
{ value: "game-event-logs:update", label: "Update" },
|
{ value: "game-event-logs:update", label: "Update" },
|
||||||
{ value: "game-event-logs:delete", label: "Delete" },
|
{ value: "game-event-logs:delete", label: "Delete" },
|
||||||
|
{ value: "admin:game-event-logs:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -392,6 +470,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "maps:read", label: "Read" },
|
{ value: "maps:read", label: "Read" },
|
||||||
{ value: "maps:update", label: "Update" },
|
{ value: "maps:update", label: "Update" },
|
||||||
{ value: "maps:delete", label: "Delete" },
|
{ value: "maps:delete", label: "Delete" },
|
||||||
|
{ value: "admin:maps:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -401,6 +480,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "narrative-events:read", label: "Read" },
|
{ value: "narrative-events:read", label: "Read" },
|
||||||
{ value: "narrative-events:update", label: "Update" },
|
{ value: "narrative-events:update", label: "Update" },
|
||||||
{ value: "narrative-events:delete", label: "Delete" },
|
{ value: "narrative-events:delete", label: "Delete" },
|
||||||
|
{ value: "admin:narrative-events:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -412,6 +492,7 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "mission-files:read", label: "Read" },
|
{ value: "mission-files:read", label: "Read" },
|
||||||
{ value: "mission-files:update", label: "Update" },
|
{ value: "mission-files:update", label: "Update" },
|
||||||
{ value: "mission-files:delete", label: "Delete" },
|
{ value: "mission-files:delete", label: "Delete" },
|
||||||
|
{ value: "admin:mission-files:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -421,6 +502,37 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "mod-lists:read", label: "Read" },
|
{ value: "mod-lists:read", label: "Read" },
|
||||||
{ value: "mod-lists:update", label: "Update" },
|
{ value: "mod-lists:update", label: "Update" },
|
||||||
{ value: "mod-lists:delete", label: "Delete" },
|
{ value: "mod-lists:delete", label: "Delete" },
|
||||||
|
{ value: "admin:mod-lists:manage", label: "Manage Admin Page" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: "Game Servers",
|
||||||
|
permissions: [
|
||||||
|
{ value: "game-servers:create", label: "Create" },
|
||||||
|
{ value: "game-servers:read", label: "Read" },
|
||||||
|
{ value: "game-servers:update", label: "Update" },
|
||||||
|
{ value: "game-servers:delete", label: "Delete" },
|
||||||
|
{ value: "admin:game-servers:manage", label: "Manage Admin Page" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: "Arma Sync Events",
|
||||||
|
permissions: [
|
||||||
|
{ value: "arma-sync-events:create", label: "Create" },
|
||||||
|
{ value: "arma-sync-events:read", label: "Read" },
|
||||||
|
{ value: "arma-sync-events:update", label: "Update" },
|
||||||
|
{ value: "arma-sync-events:delete", label: "Delete" },
|
||||||
|
{ value: "admin:arma-sync-events:manage", label: "Manage Admin Page" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: "Arma Commands",
|
||||||
|
permissions: [
|
||||||
|
{ value: "arma-commands:create", label: "Create" },
|
||||||
|
{ value: "arma-commands:read", label: "Read" },
|
||||||
|
{ value: "arma-commands:update", label: "Update" },
|
||||||
|
{ value: "arma-commands:delete", label: "Delete" },
|
||||||
|
{ value: "admin:arma-commands:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -478,182 +590,256 @@ export const ALL_PERMISSION_VALUES = PERMISSION_GROUPS.flatMap((g) =>
|
||||||
g.permissions.map((p) => p.value),
|
g.permissions.map((p) => p.value),
|
||||||
) as unknown as readonly [
|
) as unknown as readonly [
|
||||||
"system:admin-access",
|
"system:admin-access",
|
||||||
|
"media:create",
|
||||||
|
"media:read",
|
||||||
|
"media:update",
|
||||||
|
"media:delete",
|
||||||
|
"admin:media:manage",
|
||||||
"users:create",
|
"users:create",
|
||||||
"users:read",
|
"users:read",
|
||||||
"users:update",
|
"users:update",
|
||||||
"users:delete",
|
"users:delete",
|
||||||
"users:unlock",
|
"users:unlock",
|
||||||
"users:assign-roles",
|
"users:assign-roles",
|
||||||
|
"admin:users:manage",
|
||||||
|
"roles:create",
|
||||||
|
"roles:read",
|
||||||
|
"roles:update",
|
||||||
|
"roles:delete",
|
||||||
|
"admin:roles:manage",
|
||||||
"ranks:create",
|
"ranks:create",
|
||||||
"ranks:read",
|
"ranks:read",
|
||||||
"ranks:update",
|
"ranks:update",
|
||||||
"ranks:delete",
|
"ranks:delete",
|
||||||
|
"admin:ranks:manage",
|
||||||
"profiles:create",
|
"profiles:create",
|
||||||
"profiles:read",
|
"profiles:read",
|
||||||
"profiles:update",
|
"profiles:update",
|
||||||
"profiles:delete",
|
"profiles:delete",
|
||||||
|
"admin:profiles:manage",
|
||||||
"awards:create",
|
"awards:create",
|
||||||
"awards:read",
|
"awards:read",
|
||||||
"awards:update",
|
"awards:update",
|
||||||
"awards:delete",
|
"awards:delete",
|
||||||
|
"admin:awards:manage",
|
||||||
"qualifications:create",
|
"qualifications:create",
|
||||||
"qualifications:read",
|
"qualifications:read",
|
||||||
"qualifications:update",
|
"qualifications:update",
|
||||||
"qualifications:delete",
|
"qualifications:delete",
|
||||||
|
"admin:qualifications:manage",
|
||||||
"assignments:create",
|
"assignments:create",
|
||||||
"assignments:read",
|
"assignments:read",
|
||||||
"assignments:update",
|
"assignments:update",
|
||||||
"assignments:delete",
|
"assignments:delete",
|
||||||
|
"admin:assignments:manage",
|
||||||
|
"assignment-transfers:create",
|
||||||
|
"assignment-transfers:read",
|
||||||
|
"assignment-transfers:update",
|
||||||
|
"assignment-transfers:delete",
|
||||||
|
"admin:assignment-transfers:manage",
|
||||||
"experience:create",
|
"experience:create",
|
||||||
"experience:read",
|
"experience:read",
|
||||||
"experience:update",
|
"experience:update",
|
||||||
"experience:delete",
|
"experience:delete",
|
||||||
|
"admin:experience:manage",
|
||||||
"user-notifications:create",
|
"user-notifications:create",
|
||||||
"user-notifications:read",
|
"user-notifications:read",
|
||||||
"user-notifications:update",
|
"user-notifications:update",
|
||||||
"user-notifications:delete",
|
"user-notifications:delete",
|
||||||
|
"admin:user-notifications:manage",
|
||||||
"evaluations:create",
|
"evaluations:create",
|
||||||
"evaluations:read",
|
"evaluations:read",
|
||||||
"evaluations:update",
|
"evaluations:update",
|
||||||
"evaluations:delete",
|
"evaluations:delete",
|
||||||
|
"admin:evaluations:manage",
|
||||||
"missions:create",
|
"missions:create",
|
||||||
"missions:read",
|
"missions:read",
|
||||||
"missions:update",
|
"missions:update",
|
||||||
"missions:delete",
|
"missions:delete",
|
||||||
"missions:read-sensitive",
|
"missions:read-sensitive",
|
||||||
|
"admin:missions:manage",
|
||||||
"mission-attendances:create",
|
"mission-attendances:create",
|
||||||
"mission-attendances:read",
|
"mission-attendances:read",
|
||||||
"mission-attendances:update",
|
"mission-attendances:update",
|
||||||
"mission-attendances:delete",
|
"mission-attendances:delete",
|
||||||
|
"admin:mission-attendances:manage",
|
||||||
"campaigns:create",
|
"campaigns:create",
|
||||||
"campaigns:read",
|
"campaigns:read",
|
||||||
"campaigns:update",
|
"campaigns:update",
|
||||||
"campaigns:delete",
|
"campaigns:delete",
|
||||||
|
"admin:campaigns:manage",
|
||||||
"campaigns:manage",
|
"campaigns:manage",
|
||||||
"factions:create",
|
"factions:create",
|
||||||
"factions:read",
|
"factions:read",
|
||||||
"factions:update",
|
"factions:update",
|
||||||
"factions:delete",
|
"factions:delete",
|
||||||
|
"admin:factions:manage",
|
||||||
"technologies:create",
|
"technologies:create",
|
||||||
"technologies:read",
|
"technologies:read",
|
||||||
"technologies:update",
|
"technologies:update",
|
||||||
"technologies:delete",
|
"technologies:delete",
|
||||||
|
"admin:technologies:manage",
|
||||||
"assets:create",
|
"assets:create",
|
||||||
"assets:read",
|
"assets:read",
|
||||||
"assets:update",
|
"assets:update",
|
||||||
"assets:delete",
|
"assets:delete",
|
||||||
|
"admin:assets:manage",
|
||||||
"resources:create",
|
"resources:create",
|
||||||
"resources:read",
|
"resources:read",
|
||||||
"resources:update",
|
"resources:update",
|
||||||
"resources:delete",
|
"resources:delete",
|
||||||
|
"admin:resources:manage",
|
||||||
"vehicles:create",
|
"vehicles:create",
|
||||||
"vehicles:read",
|
"vehicles:read",
|
||||||
"vehicles:update",
|
"vehicles:update",
|
||||||
"vehicles:delete",
|
"vehicles:delete",
|
||||||
|
"admin:vehicles:manage",
|
||||||
"structures:create",
|
"structures:create",
|
||||||
"structures:read",
|
"structures:read",
|
||||||
"structures:update",
|
"structures:update",
|
||||||
"structures:delete",
|
"structures:delete",
|
||||||
|
"admin:structures:manage",
|
||||||
"shipments:create",
|
"shipments:create",
|
||||||
"shipments:read",
|
"shipments:read",
|
||||||
"shipments:update",
|
"shipments:update",
|
||||||
"shipments:delete",
|
"shipments:delete",
|
||||||
|
"admin:shipments:manage",
|
||||||
"bank-accounts:create",
|
"bank-accounts:create",
|
||||||
"bank-accounts:read",
|
"bank-accounts:read",
|
||||||
"bank-accounts:update",
|
"bank-accounts:update",
|
||||||
"bank-accounts:delete",
|
"bank-accounts:delete",
|
||||||
|
"admin:bank-accounts:manage",
|
||||||
"bank-transactions:create",
|
"bank-transactions:create",
|
||||||
"bank-transactions:read",
|
"bank-transactions:read",
|
||||||
"bank-transactions:update",
|
"bank-transactions:update",
|
||||||
"bank-transactions:delete",
|
"bank-transactions:delete",
|
||||||
|
"admin:bank-transactions:manage",
|
||||||
"ledger-entries:create",
|
"ledger-entries:create",
|
||||||
"ledger-entries:read",
|
"ledger-entries:read",
|
||||||
"ledger-entries:update",
|
"ledger-entries:update",
|
||||||
"ledger-entries:delete",
|
"ledger-entries:delete",
|
||||||
|
"admin:ledger-entries:manage",
|
||||||
"locker-storages:create",
|
"locker-storages:create",
|
||||||
"locker-storages:read",
|
"locker-storages:read",
|
||||||
"locker-storages:update",
|
"locker-storages:update",
|
||||||
"locker-storages:delete",
|
"locker-storages:delete",
|
||||||
|
"admin:locker-storages:manage",
|
||||||
"loadouts:create",
|
"loadouts:create",
|
||||||
"loadouts:read",
|
"loadouts:read",
|
||||||
"loadouts:update",
|
"loadouts:update",
|
||||||
"loadouts:delete",
|
"loadouts:delete",
|
||||||
|
"admin:loadouts:manage",
|
||||||
"market-listings:create",
|
"market-listings:create",
|
||||||
"market-listings:read",
|
"market-listings:read",
|
||||||
"market-listings:update",
|
"market-listings:update",
|
||||||
"market-listings:delete",
|
"market-listings:delete",
|
||||||
|
"admin:market-listings:manage",
|
||||||
"market-negotiations:create",
|
"market-negotiations:create",
|
||||||
"market-negotiations:read",
|
"market-negotiations:read",
|
||||||
"market-negotiations:update",
|
"market-negotiations:update",
|
||||||
"market-negotiations:delete",
|
"market-negotiations:delete",
|
||||||
|
"admin:market-negotiations:manage",
|
||||||
"projects:create",
|
"projects:create",
|
||||||
"projects:read",
|
"projects:read",
|
||||||
"projects:update",
|
"projects:update",
|
||||||
"projects:delete",
|
"projects:delete",
|
||||||
"sprints:create",
|
"admin:projects:manage",
|
||||||
"sprints:read",
|
|
||||||
"sprints:update",
|
|
||||||
"sprints:delete",
|
|
||||||
"labels:create",
|
"labels:create",
|
||||||
"labels:read",
|
"labels:read",
|
||||||
"labels:update",
|
"labels:update",
|
||||||
"labels:delete",
|
"labels:delete",
|
||||||
|
"admin:labels:manage",
|
||||||
|
"sprints:create",
|
||||||
|
"sprints:read",
|
||||||
|
"sprints:update",
|
||||||
|
"sprints:delete",
|
||||||
|
"admin:sprints:manage",
|
||||||
"releases:create",
|
"releases:create",
|
||||||
"releases:read",
|
"releases:read",
|
||||||
"releases:update",
|
"releases:update",
|
||||||
"releases:delete",
|
"releases:delete",
|
||||||
|
"admin:releases:manage",
|
||||||
"tickets:create",
|
"tickets:create",
|
||||||
"tickets:read",
|
"tickets:read",
|
||||||
"tickets:update",
|
"tickets:update",
|
||||||
"tickets:delete",
|
"tickets:delete",
|
||||||
|
"admin:tickets:manage",
|
||||||
|
"ticket-votes:create",
|
||||||
|
"ticket-votes:read",
|
||||||
|
"ticket-votes:update",
|
||||||
|
"ticket-votes:delete",
|
||||||
|
"admin:ticket-votes:manage",
|
||||||
"game-structures:create",
|
"game-structures:create",
|
||||||
"game-structures:read",
|
"game-structures:read",
|
||||||
"game-structures:update",
|
"game-structures:update",
|
||||||
"game-structures:delete",
|
"game-structures:delete",
|
||||||
|
"admin:game-structures:manage",
|
||||||
"game-vehicles:create",
|
"game-vehicles:create",
|
||||||
"game-vehicles:read",
|
"game-vehicles:read",
|
||||||
"game-vehicles:update",
|
"game-vehicles:update",
|
||||||
"game-vehicles:delete",
|
"game-vehicles:delete",
|
||||||
|
"admin:game-vehicles:manage",
|
||||||
"game-npcs:create",
|
"game-npcs:create",
|
||||||
"game-npcs:read",
|
"game-npcs:read",
|
||||||
"game-npcs:update",
|
"game-npcs:update",
|
||||||
"game-npcs:delete",
|
"game-npcs:delete",
|
||||||
|
"admin:game-npcs:manage",
|
||||||
"game-hard-resources:create",
|
"game-hard-resources:create",
|
||||||
"game-hard-resources:read",
|
"game-hard-resources:read",
|
||||||
"game-hard-resources:update",
|
"game-hard-resources:update",
|
||||||
"game-hard-resources:delete",
|
"game-hard-resources:delete",
|
||||||
|
"admin:game-hard-resources:manage",
|
||||||
"game-event-logs:create",
|
"game-event-logs:create",
|
||||||
"game-event-logs:read",
|
"game-event-logs:read",
|
||||||
"game-event-logs:update",
|
"game-event-logs:update",
|
||||||
"game-event-logs:delete",
|
"game-event-logs:delete",
|
||||||
|
"admin:game-event-logs:manage",
|
||||||
"maps:create",
|
"maps:create",
|
||||||
"maps:read",
|
"maps:read",
|
||||||
"maps:update",
|
"maps:update",
|
||||||
"maps:delete",
|
"maps:delete",
|
||||||
|
"admin:maps:manage",
|
||||||
"narrative-events:create",
|
"narrative-events:create",
|
||||||
"narrative-events:read",
|
"narrative-events:read",
|
||||||
"narrative-events:update",
|
"narrative-events:update",
|
||||||
"narrative-events:delete",
|
"narrative-events:delete",
|
||||||
|
"admin:narrative-events:manage",
|
||||||
"mission-files:create",
|
"mission-files:create",
|
||||||
"mission-files:read",
|
"mission-files:read",
|
||||||
"mission-files:update",
|
"mission-files:update",
|
||||||
"mission-files:delete",
|
"mission-files:delete",
|
||||||
|
"admin:mission-files:manage",
|
||||||
"mod-lists:create",
|
"mod-lists:create",
|
||||||
"mod-lists:read",
|
"mod-lists:read",
|
||||||
"mod-lists:update",
|
"mod-lists:update",
|
||||||
"mod-lists:delete",
|
"mod-lists:delete",
|
||||||
|
"admin:mod-lists:manage",
|
||||||
|
"game-servers:create",
|
||||||
|
"game-servers:read",
|
||||||
|
"game-servers:update",
|
||||||
|
"game-servers:delete",
|
||||||
|
"admin:game-servers:manage",
|
||||||
|
"arma-sync-events:create",
|
||||||
|
"arma-sync-events:read",
|
||||||
|
"arma-sync-events:update",
|
||||||
|
"arma-sync-events:delete",
|
||||||
|
"admin:arma-sync-events:manage",
|
||||||
|
"arma-commands:create",
|
||||||
|
"arma-commands:read",
|
||||||
|
"arma-commands:update",
|
||||||
|
"arma-commands:delete",
|
||||||
|
"admin:arma-commands:manage",
|
||||||
"game-rules:read",
|
"game-rules:read",
|
||||||
"game-rules:update",
|
"game-rules:update",
|
||||||
"shims:read",
|
"shims:read",
|
||||||
"shims:update",
|
"shims:update",
|
||||||
"campaigns:manage",
|
|
||||||
"logistics:manage",
|
"logistics:manage",
|
||||||
|
"intelligence:manage",
|
||||||
|
"profiles:intel_excerpt:update",
|
||||||
"banking:manage",
|
"banking:manage",
|
||||||
"tickets:staff",
|
"tickets:staff",
|
||||||
"discord:staff",
|
"discord:staff",
|
||||||
"discord:announce",
|
"discord:announce",
|
||||||
"intelligence:manage",
|
|
||||||
"profiles:intel_excerpt:update",
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,17 @@ import { getPayload } from "payload";
|
||||||
import config from "@payload-config";
|
import config from "@payload-config";
|
||||||
import type { Permission } from "@/permissions";
|
import type { Permission } from "@/permissions";
|
||||||
|
|
||||||
|
const resolvedConfig = await config;
|
||||||
|
const COLLECTION_SLUGS = resolvedConfig.collections.map((c) => c.slug);
|
||||||
|
|
||||||
|
const ALL_COLLECTION_READS: Permission[] = COLLECTION_SLUGS.map(
|
||||||
|
(slug) => `${slug}:read` as Permission,
|
||||||
|
);
|
||||||
|
|
||||||
|
const ALL_ADMIN_PAGE_MANAGE: Permission[] = COLLECTION_SLUGS.map(
|
||||||
|
(slug) => `admin:${slug}:manage` as Permission,
|
||||||
|
);
|
||||||
|
|
||||||
const USER_PERMISSIONS: Permission[] = [
|
const USER_PERMISSIONS: Permission[] = [
|
||||||
"users:read",
|
"users:read",
|
||||||
"ranks:read",
|
"ranks:read",
|
||||||
|
|
@ -47,34 +58,38 @@ const USER_PERMISSIONS: Permission[] = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const ADMIN_PERMISSIONS: Permission[] = [
|
const ADMIN_PERMISSIONS: Permission[] = [
|
||||||
...USER_PERMISSIONS,
|
...new Set<Permission>([
|
||||||
"system:admin-access",
|
...USER_PERMISSIONS,
|
||||||
"users:read",
|
"system:admin-access",
|
||||||
"users:update",
|
"users:read",
|
||||||
"users:delete",
|
"users:update",
|
||||||
"technologies:create",
|
"users:delete",
|
||||||
"technologies:read",
|
"technologies:create",
|
||||||
"technologies:update",
|
"technologies:read",
|
||||||
"technologies:delete",
|
"technologies:update",
|
||||||
"tickets:read",
|
"technologies:delete",
|
||||||
"tickets:update",
|
"tickets:read",
|
||||||
"tickets:staff",
|
"tickets:update",
|
||||||
"bank-accounts:create",
|
"tickets:staff",
|
||||||
"bank-accounts:update",
|
"bank-accounts:create",
|
||||||
"user-notifications:read",
|
"bank-accounts:update",
|
||||||
"user-notifications:update",
|
"user-notifications:read",
|
||||||
"mission-attendances:create",
|
"user-notifications:update",
|
||||||
"mission-attendances:read",
|
"mission-attendances:create",
|
||||||
"mission-attendances:update",
|
"mission-attendances:read",
|
||||||
"mission-attendances:delete",
|
"mission-attendances:update",
|
||||||
"missions:read",
|
"mission-attendances:delete",
|
||||||
"market-negotiations:read",
|
"missions:read",
|
||||||
"market-negotiations:update",
|
"market-negotiations:read",
|
||||||
"logistics:manage",
|
"market-negotiations:update",
|
||||||
"banking:manage",
|
"logistics:manage",
|
||||||
"discord:staff",
|
"banking:manage",
|
||||||
"discord:announce",
|
"discord:staff",
|
||||||
"structures:create",
|
"discord:announce",
|
||||||
|
"structures:create",
|
||||||
|
...ALL_COLLECTION_READS,
|
||||||
|
...ALL_ADMIN_PAGE_MANAGE,
|
||||||
|
]),
|
||||||
];
|
];
|
||||||
|
|
||||||
interface BuiltinRole {
|
interface BuiltinRole {
|
||||||
|
|
@ -145,9 +160,36 @@ export const seedRoles = async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (existing.docs.length > 0) {
|
if (existing.docs.length > 0) {
|
||||||
const doc = existing.docs[0] as { id: number };
|
const doc = existing.docs[0] as {
|
||||||
|
id: number;
|
||||||
|
isSystem?: boolean;
|
||||||
|
permissions?: Permission[] | null;
|
||||||
|
};
|
||||||
roleIdMap.set(role.slug, doc.id);
|
roleIdMap.set(role.slug, doc.id);
|
||||||
payload.logger.info(` Role "${role.slug}" already exists (id=${doc.id}), skipping.`);
|
|
||||||
|
if (role.isSystem) {
|
||||||
|
const existingPerms = doc.permissions ?? [];
|
||||||
|
const missing = role.permissions.filter((p) => !existingPerms.includes(p));
|
||||||
|
if (missing.length > 0) {
|
||||||
|
await payload.update({
|
||||||
|
collection: "roles",
|
||||||
|
id: doc.id,
|
||||||
|
data: { permissions: [...existingPerms, ...missing] },
|
||||||
|
overrideAccess: true,
|
||||||
|
});
|
||||||
|
payload.logger.info(
|
||||||
|
` Role "${role.slug}" (id=${doc.id}) updated with ${missing.length} missing built-in permission(s).`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
payload.logger.info(
|
||||||
|
` Role "${role.slug}" already exists (id=${doc.id}), permissions up to date.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
payload.logger.info(
|
||||||
|
` Role "${role.slug}" already exists (id=${doc.id}), skipping (non-system).`,
|
||||||
|
);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import type { Payload, PayloadRequest } from "payload";
|
import type { Access, AccessArgs, AccessResult, Payload, PayloadRequest } from "payload";
|
||||||
import { loadUserPermissions } from "@/utils/access-control/loadUserPermissions";
|
import { loadUserPermissions } from "@/utils/access-control/loadUserPermissions";
|
||||||
import type { Permission } from "@/permissions";
|
import type { Permission } from "@/permissions";
|
||||||
|
|
||||||
|
|
@ -80,6 +80,65 @@ export async function hasAllPermissions(
|
||||||
return permissions.every((p) => userPerms.has(p));
|
return permissions.every((p) => userPerms.has(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect whether a request targets the Payload admin panel.
|
||||||
|
*
|
||||||
|
* The admin panel is served under `config.routes.admin` (default `/admin`).
|
||||||
|
* `req.pathname` is the request URL pathname (set by Payload's request
|
||||||
|
* creation), so this reliably distinguishes admin-panel requests from REST
|
||||||
|
* API requests — unlike `x-invoke-path`, which is unreliable in layouts.
|
||||||
|
* Local API calls (server actions, seeds, MCP, bots) get a non-admin
|
||||||
|
* pathname and therefore keep the original access behavior.
|
||||||
|
*/
|
||||||
|
function isAdminPanelRequest(req: PayloadRequest): boolean {
|
||||||
|
const adminRoute = req.payload.config.routes?.admin ?? "/admin";
|
||||||
|
const pathname = req.pathname;
|
||||||
|
if (!pathname) return false;
|
||||||
|
return pathname === adminRoute || pathname.startsWith(`${adminRoute}/`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory that wraps a collection's `read` access so that the Payload admin
|
||||||
|
* panel only shows/interacts with the collection when the user holds BOTH
|
||||||
|
* `<slug>:read` AND `admin:<slug>:manage`. Non-admin (REST/API) reads keep
|
||||||
|
* the original access behavior unchanged.
|
||||||
|
*
|
||||||
|
* Apply centrally to every collection in `payload.config.ts`:
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* collections: collections.map((collection) => ({
|
||||||
|
* ...collection,
|
||||||
|
* access: {
|
||||||
|
* ...collection.access,
|
||||||
|
* read: requireAdminPageAccess(collection.slug, collection.access?.read),
|
||||||
|
* },
|
||||||
|
* }))
|
||||||
|
*/
|
||||||
|
export function requireAdminPageAccess(collectionSlug: string, readAccess?: Access | boolean) {
|
||||||
|
return async (args: AccessArgs): Promise<AccessResult> => {
|
||||||
|
const { req } = args;
|
||||||
|
|
||||||
|
if (isAdminPanelRequest(req)) {
|
||||||
|
// Admin panel: require both the collection read permission and the
|
||||||
|
// admin page-manage permission. Must return a plain boolean — a `Where`
|
||||||
|
// here would be treated as "no permission" by the admin UI.
|
||||||
|
return hasAllPermissions(
|
||||||
|
req.payload,
|
||||||
|
req.user,
|
||||||
|
`${collectionSlug}:read` as Permission,
|
||||||
|
`admin:${collectionSlug}:manage` as Permission,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-admin (REST/API): preserve the original read access behavior.
|
||||||
|
if (typeof readAccess === "function") return readAccess(args);
|
||||||
|
if (readAccess === true) return true;
|
||||||
|
if (readAccess === false) return false;
|
||||||
|
// Omitted read access → Payload default: any logged-in user can read.
|
||||||
|
return Boolean(req.user);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory that creates a Payload collection access function requiring a specific
|
* Factory that creates a Payload collection access function requiring a specific
|
||||||
* permission. Drop-in replacement for `isDeveloper` / `isAdmin` in collection
|
* permission. Drop-in replacement for `isDeveloper` / `isAdmin` in collection
|
||||||
|
|
|
||||||
164
tests/int/admin-page-access.int.spec.ts
Normal file
164
tests/int/admin-page-access.int.spec.ts
Normal file
|
|
@ -0,0 +1,164 @@
|
||||||
|
import { getPayload, Payload } from "payload";
|
||||||
|
import type { Access } from "payload";
|
||||||
|
import config from "@/payload.config";
|
||||||
|
|
||||||
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import type { Role, User } from "@/payload-types";
|
||||||
|
import { requireAdminPageAccess } from "@/utils/access-control/hasPermission";
|
||||||
|
import { invalidatePermissionCache } from "@/utils/access-control/loadUserPermissions";
|
||||||
|
|
||||||
|
let payload: Payload;
|
||||||
|
|
||||||
|
const RUN = `adm-${Date.now().toString(36)}`;
|
||||||
|
const TIMEOUT = 30_000;
|
||||||
|
|
||||||
|
describe("Scoped admin page access control", () => {
|
||||||
|
const roleIds: number[] = [];
|
||||||
|
const userIds: number[] = [];
|
||||||
|
|
||||||
|
let readOnlyUser: User;
|
||||||
|
let manageOnlyUser: User;
|
||||||
|
let bothUser: User;
|
||||||
|
let devUser: User;
|
||||||
|
let neitherUser: User;
|
||||||
|
|
||||||
|
const makeRole = async (label: string, extra: Partial<Role> = {}): Promise<Role> => {
|
||||||
|
const role = (await payload.create({
|
||||||
|
collection: "roles",
|
||||||
|
data: { name: `${RUN}-${label}`, slug: `${RUN}-${label}`, ...extra },
|
||||||
|
overrideAccess: true,
|
||||||
|
depth: 0,
|
||||||
|
})) as unknown as Role;
|
||||||
|
roleIds.push(role.id);
|
||||||
|
return role;
|
||||||
|
};
|
||||||
|
|
||||||
|
const makeUser = async (label: string, roleId: number): Promise<User> => {
|
||||||
|
const user = (await payload.create({
|
||||||
|
collection: "users",
|
||||||
|
data: {
|
||||||
|
username: `${RUN}-${label}`,
|
||||||
|
discordUsername: `${RUN}-${label}`,
|
||||||
|
displayName: label.toUpperCase(),
|
||||||
|
steamId: `7656119${Math.floor(Math.random() * 1e9)}`,
|
||||||
|
password: "Test123",
|
||||||
|
// Permission resolution reads roleDocs (the dynamic RBAC relationship), not
|
||||||
|
// the legacy `roles` enum — so assign a real role doc to grant permissions.
|
||||||
|
roleDocs: [roleId],
|
||||||
|
},
|
||||||
|
overrideAccess: true,
|
||||||
|
depth: 0,
|
||||||
|
})) as unknown as User;
|
||||||
|
userIds.push(user.id);
|
||||||
|
return user;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Invoke the admin-page wrapper exactly as Payload would for an admin-panel
|
||||||
|
// request (pathname under /admin). The vitest environment has no Next.js HTTP
|
||||||
|
// server, so calling the access function directly is the standard way to unit-test
|
||||||
|
// Payload access control.
|
||||||
|
const adminDecision = async (user: User | null): Promise<unknown> => {
|
||||||
|
const fn = requireAdminPageAccess("missions");
|
||||||
|
return await (fn as (args: { req: unknown }) => Promise<unknown>)({
|
||||||
|
req: { user, payload, pathname: "/admin/collections/missions" },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Same wrapper, but on a REST API pathname — the original read access must be
|
||||||
|
// preserved unchanged outside the admin panel.
|
||||||
|
const apiDecision = async (user: User | null, readAccess?: Access | boolean): Promise<unknown> => {
|
||||||
|
const fn = requireAdminPageAccess("missions", readAccess);
|
||||||
|
return await (fn as (args: { req: unknown }) => Promise<unknown>)({
|
||||||
|
req: { user, payload, pathname: "/api/missions" },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const payloadConfig = await config;
|
||||||
|
payload = await getPayload({ config: payloadConfig });
|
||||||
|
invalidatePermissionCache();
|
||||||
|
|
||||||
|
const readOnlyRole = await makeRole("readonly", { permissions: ["missions:read"] });
|
||||||
|
const manageOnlyRole = await makeRole("manageonly", {
|
||||||
|
permissions: ["admin:missions:manage"],
|
||||||
|
});
|
||||||
|
const bothRole = await makeRole("both", {
|
||||||
|
permissions: ["missions:read", "admin:missions:manage"],
|
||||||
|
});
|
||||||
|
const devRole = await makeRole("dev", { isSuperuser: true });
|
||||||
|
const neitherRole = await makeRole("neither", { permissions: [] });
|
||||||
|
|
||||||
|
readOnlyUser = await makeUser("readonly", readOnlyRole.id);
|
||||||
|
manageOnlyUser = await makeUser("manageonly", manageOnlyRole.id);
|
||||||
|
bothUser = await makeUser("both", bothRole.id);
|
||||||
|
devUser = await makeUser("dev", devRole.id);
|
||||||
|
neitherUser = await makeUser("neither", neitherRole.id);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
if (!payload) return;
|
||||||
|
for (const id of userIds) {
|
||||||
|
const profiles = await payload
|
||||||
|
.find({
|
||||||
|
collection: "profiles",
|
||||||
|
where: { user: { equals: id } },
|
||||||
|
limit: 5,
|
||||||
|
depth: 0,
|
||||||
|
overrideAccess: true,
|
||||||
|
})
|
||||||
|
.catch(() => null);
|
||||||
|
for (const p of profiles?.docs ?? []) {
|
||||||
|
await Promise.allSettled([
|
||||||
|
payload.delete({ collection: "profiles", id: p.id, overrideAccess: true }),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
await Promise.allSettled([payload.delete({ collection: "users", id, overrideAccess: true })]);
|
||||||
|
}
|
||||||
|
for (const id of roleIds) {
|
||||||
|
await Promise.allSettled([payload.delete({ collection: "roles", id, overrideAccess: true })]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("denies admin access with only the collection read permission", async () => {
|
||||||
|
expect(await adminDecision(readOnlyUser)).toBe(false);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("denies admin access with only the admin page-manage permission", async () => {
|
||||||
|
expect(await adminDecision(manageOnlyUser)).toBe(false);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("grants admin access with both permissions", async () => {
|
||||||
|
expect(await adminDecision(bothUser)).toBe(true);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("grants admin access to superuser roles", async () => {
|
||||||
|
expect(await adminDecision(devUser)).toBe(true);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("denies admin access with neither permission", async () => {
|
||||||
|
expect(await adminDecision(neitherUser)).toBe(false);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("denies anonymous admin access", async () => {
|
||||||
|
expect(await adminDecision(null)).toBe(false);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("preserves the default logged-in read on the API path", async () => {
|
||||||
|
expect(await apiDecision(bothUser)).toBe(true);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("preserves the default anonymous denial on the API path", async () => {
|
||||||
|
expect(await apiDecision(null)).toBe(false);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("delegates a Where-returning original access on the API path", async () => {
|
||||||
|
const where = { user: { equals: bothUser.id } };
|
||||||
|
expect(await apiDecision(bothUser, () => where)).toEqual(where);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("delegates a boolean original access on the API path", async () => {
|
||||||
|
expect(await apiDecision(bothUser, () => false)).toBe(false);
|
||||||
|
expect(await apiDecision(bothUser, () => true)).toBe(true);
|
||||||
|
}, TIMEOUT);
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue