chore(seed): update seed scripts for RBAC roles
Assign the developer role document to the sysadmin user on seed. Add seedRoles script to create default RBAC roles.
This commit is contained in:
parent
d3890cc191
commit
fb74e6b0c2
2 changed files with 236 additions and 0 deletions
226
src/tools/seed/seedRoles.ts
Normal file
226
src/tools/seed/seedRoles.ts
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
import { getPayload } from "payload";
|
||||
import config from "@payload-config";
|
||||
import type { Permission } from "@/permissions";
|
||||
|
||||
const USER_PERMISSIONS: Permission[] = [
|
||||
"users:read",
|
||||
"ranks:read",
|
||||
"profiles:read",
|
||||
"awards:read",
|
||||
"qualifications:read",
|
||||
"assignments:read",
|
||||
"experience:read",
|
||||
"user-notifications:read",
|
||||
"missions:read",
|
||||
"mission-attendances:read",
|
||||
"campaigns:read",
|
||||
"factions:read",
|
||||
"technologies:read",
|
||||
"assets:read",
|
||||
"resources:read",
|
||||
"vehicles:read",
|
||||
"structures:read",
|
||||
"shipments:read",
|
||||
"bank-accounts:read",
|
||||
"bank-transactions:read",
|
||||
"ledger-entries:read",
|
||||
"locker-storages:read",
|
||||
"loadouts:read",
|
||||
"market-listings:read",
|
||||
"market-negotiations:read",
|
||||
"tickets:read",
|
||||
"game-structures:read",
|
||||
"game-vehicles:read",
|
||||
"game-npcs:read",
|
||||
"game-hard-resources:read",
|
||||
"game-event-logs:read",
|
||||
"maps:read",
|
||||
"narrative-events:read",
|
||||
"mission-files:read",
|
||||
"mod-lists:read",
|
||||
"game-rules:read",
|
||||
"shims:read",
|
||||
"shipments:create",
|
||||
"shipments:update",
|
||||
"structures:update",
|
||||
"structures:delete",
|
||||
];
|
||||
|
||||
const ADMIN_PERMISSIONS: Permission[] = [
|
||||
...USER_PERMISSIONS,
|
||||
"system:admin-access",
|
||||
"users:read",
|
||||
"users:update",
|
||||
"users:delete",
|
||||
"technologies:create",
|
||||
"technologies:read",
|
||||
"technologies:update",
|
||||
"technologies:delete",
|
||||
"tickets:read",
|
||||
"tickets:update",
|
||||
"tickets:staff",
|
||||
"bank-accounts:create",
|
||||
"bank-accounts:update",
|
||||
"user-notifications:read",
|
||||
"user-notifications:update",
|
||||
"mission-attendances:create",
|
||||
"mission-attendances:read",
|
||||
"mission-attendances:update",
|
||||
"mission-attendances:delete",
|
||||
"missions:read",
|
||||
"market-negotiations:read",
|
||||
"market-negotiations:update",
|
||||
"logistics:manage",
|
||||
"banking:manage",
|
||||
"discord:staff",
|
||||
"discord:announce",
|
||||
"structures:create",
|
||||
];
|
||||
|
||||
interface BuiltinRole {
|
||||
slug: string;
|
||||
name: string;
|
||||
description: string;
|
||||
permissions: Permission[];
|
||||
isSystem: boolean;
|
||||
isSuperuser: boolean;
|
||||
}
|
||||
|
||||
const BUILTIN_ROLES: BuiltinRole[] = [
|
||||
{
|
||||
slug: "guest",
|
||||
name: "Guest",
|
||||
description: "Default role for unauthenticated or basic users. No permissions.",
|
||||
permissions: [],
|
||||
isSystem: true,
|
||||
isSuperuser: false,
|
||||
},
|
||||
{
|
||||
slug: "user",
|
||||
name: "User",
|
||||
description: "Standard authenticated user. Can manage shipments, structures, and read profiles.",
|
||||
permissions: [...USER_PERMISSIONS],
|
||||
isSystem: true,
|
||||
isSuperuser: false,
|
||||
},
|
||||
{
|
||||
slug: "admin",
|
||||
name: "Admin",
|
||||
description: "Administrator. Can manage users, tickets, banking, logistics, and most collections.",
|
||||
permissions: [...ADMIN_PERMISSIONS],
|
||||
isSystem: true,
|
||||
isSuperuser: false,
|
||||
},
|
||||
{
|
||||
slug: "developer",
|
||||
name: "Developer",
|
||||
description: "Superuser. Bypasses all permission checks. Full access to everything.",
|
||||
permissions: [],
|
||||
isSystem: true,
|
||||
isSuperuser: true,
|
||||
},
|
||||
];
|
||||
|
||||
const ENUM_TO_SLUG: Record<string, string> = {
|
||||
guest: "guest",
|
||||
user: "user",
|
||||
trusted: "user",
|
||||
admin: "admin",
|
||||
developer: "developer",
|
||||
};
|
||||
|
||||
export const seedRoles = async () => {
|
||||
const payload = await getPayload({ config });
|
||||
|
||||
payload.logger.info("Seeding built-in RBAC roles...");
|
||||
|
||||
const roleIdMap = new Map<string, number>();
|
||||
|
||||
for (const role of BUILTIN_ROLES) {
|
||||
const existing = await payload.find({
|
||||
collection: "roles",
|
||||
where: { slug: { equals: role.slug } },
|
||||
limit: 1,
|
||||
overrideAccess: true,
|
||||
});
|
||||
|
||||
if (existing.docs.length > 0) {
|
||||
const doc = existing.docs[0] as { id: number };
|
||||
roleIdMap.set(role.slug, doc.id);
|
||||
payload.logger.info(` Role "${role.slug}" already exists (id=${doc.id}), skipping.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const created = await payload.create({
|
||||
collection: "roles",
|
||||
data: {
|
||||
name: role.name,
|
||||
slug: role.slug,
|
||||
description: role.description,
|
||||
permissions: role.permissions,
|
||||
isSystem: role.isSystem,
|
||||
isSuperuser: role.isSuperuser,
|
||||
},
|
||||
overrideAccess: true,
|
||||
});
|
||||
|
||||
roleIdMap.set(role.slug, created.id);
|
||||
payload.logger.info(` Created role "${role.slug}" (id=${created.id}).`);
|
||||
}
|
||||
|
||||
payload.logger.info("Migrating existing users from roles enum to roleDocs...");
|
||||
|
||||
const users = await payload.find({
|
||||
collection: "users",
|
||||
limit: 0,
|
||||
depth: 0,
|
||||
overrideAccess: true,
|
||||
select: { roles: true, roleDocs: true },
|
||||
});
|
||||
|
||||
let migrated = 0;
|
||||
let skipped = 0;
|
||||
|
||||
for (const user of users.docs) {
|
||||
const u = user as { id: number; roles?: string[] | null; roleDocs?: unknown[] | null };
|
||||
|
||||
if (u.roleDocs && Array.isArray(u.roleDocs) && u.roleDocs.length > 0) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const enumRoles = u.roles ?? [];
|
||||
if (enumRoles.length === 0) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const roleDocIds: number[] = [];
|
||||
const seenSlugs = new Set<string>();
|
||||
|
||||
for (const enumRole of enumRoles) {
|
||||
const slug = ENUM_TO_SLUG[enumRole];
|
||||
if (!slug || seenSlugs.has(slug)) continue;
|
||||
seenSlugs.add(slug);
|
||||
const roleId = roleIdMap.get(slug);
|
||||
if (roleId) roleDocIds.push(roleId);
|
||||
}
|
||||
|
||||
if (roleDocIds.length === 0) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
await payload.update({
|
||||
collection: "users",
|
||||
id: user.id,
|
||||
data: { roleDocs: roleDocIds },
|
||||
overrideAccess: true,
|
||||
});
|
||||
migrated++;
|
||||
}
|
||||
|
||||
payload.logger.info(`Migration complete: ${migrated} users migrated, ${skipped} users skipped.`);
|
||||
};
|
||||
|
||||
await seedRoles();
|
||||
|
|
@ -14,6 +14,15 @@ export const seedUsers = async () => {
|
|||
},
|
||||
});
|
||||
|
||||
const devRole = await payload.find({
|
||||
collection: "roles",
|
||||
where: { slug: { equals: "developer" } },
|
||||
limit: 1,
|
||||
depth: 0,
|
||||
overrideAccess: true,
|
||||
});
|
||||
const devRoleId = devRole.docs[0]?.id;
|
||||
|
||||
payload.logger.info(`Creating initial sysadmin/developer user...`);
|
||||
try {
|
||||
const result = await payload.create({
|
||||
|
|
@ -25,6 +34,7 @@ export const seedUsers = async () => {
|
|||
discordUsername: "Z8MB1E",
|
||||
steamId: "76561198091303179",
|
||||
roles: ["developer"],
|
||||
roleDocs: devRoleId ? [devRoleId] : [],
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue