172 lines
6.4 KiB
TypeScript
172 lines
6.4 KiB
TypeScript
/**
|
|
* Prisma seed script for RayLab Core - Phase 1
|
|
* Run with: npx ts-node prisma/seed.ts
|
|
* Requires DATABASE_URL env var.
|
|
*/
|
|
import { PrismaClient } from '@prisma/client';
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
async function main() {
|
|
console.log('Seeding default roles, permissions, and mappings...');
|
|
|
|
// Default roles
|
|
const roles = [
|
|
{ name: 'Owner', displayName: 'Owner', description: 'Platform owner with full access' },
|
|
{ name: 'Employee', displayName: 'Employee', description: 'Employee role with internal permissions' },
|
|
{ name: 'Family', displayName: 'Family', description: 'Family role with limited access' },
|
|
];
|
|
|
|
for (const r of roles) {
|
|
const code = r.name.toLowerCase();
|
|
await prisma.role.upsert({
|
|
where: { code },
|
|
update: { name: r.name, displayName: r.displayName, description: r.description },
|
|
create: { code, name: r.name, displayName: r.displayName, description: r.description },
|
|
});
|
|
}
|
|
|
|
// Default permissions (resource.action lowercase as requested)
|
|
const permissions = [
|
|
'users.create',
|
|
'users.read',
|
|
'users.update',
|
|
'users.delete',
|
|
'roles.create',
|
|
'roles.read',
|
|
'roles.update',
|
|
'roles.delete',
|
|
'permissions.create',
|
|
'permissions.read',
|
|
'permissions.update',
|
|
'permissions.delete',
|
|
'audit.read',
|
|
'storage.read',
|
|
'storage.write',
|
|
'storage.delete',
|
|
'media.read',
|
|
'media.write',
|
|
'media.delete',
|
|
'scheduler.read',
|
|
'scheduler.manage',
|
|
];
|
|
|
|
for (const p of permissions) {
|
|
// Use code as canonical identifier (resource.action). Use name == code for now.
|
|
const code = p;
|
|
await prisma.permission.upsert({
|
|
where: { code },
|
|
update: { name: p, description: null },
|
|
create: { code, name: p, description: null },
|
|
});
|
|
}
|
|
|
|
// Assign permissions to roles (example mapping)
|
|
const owner = await prisma.role.findUnique({ where: { code: 'owner' } });
|
|
const employee = await prisma.role.findUnique({ where: { code: 'employee' } });
|
|
const family = await prisma.role.findUnique({ where: { code: 'family' } });
|
|
|
|
if (!owner || !employee || !family) {
|
|
throw new Error('Default roles missing after upsert');
|
|
}
|
|
|
|
// Owner -> all permissions
|
|
const allPerms = await prisma.permission.findMany();
|
|
for (const perm of allPerms) {
|
|
await prisma.rolePermission.upsert({
|
|
where: { roleId_permissionId: { roleId: owner.id, permissionId: perm.id } },
|
|
update: {},
|
|
create: { roleId: owner.id, permissionId: perm.id },
|
|
});
|
|
}
|
|
|
|
// Employee -> a subset
|
|
const employeePerms = [
|
|
'users.read',
|
|
'permissions.read',
|
|
'scheduler.read',
|
|
'scheduler.manage',
|
|
'media.read',
|
|
'media.write',
|
|
];
|
|
for (const p of employeePerms) {
|
|
const perm = await prisma.permission.findUnique({ where: { code: p } });
|
|
if (perm) {
|
|
await prisma.rolePermission.upsert({
|
|
where: { roleId_permissionId: { roleId: employee.id, permissionId: perm.id } },
|
|
update: {},
|
|
create: { roleId: employee.id, permissionId: perm.id },
|
|
});
|
|
}
|
|
}
|
|
|
|
// Family -> limited
|
|
const familyPerms = [
|
|
'media.read',
|
|
'media.write',
|
|
'media.delete',
|
|
];
|
|
for (const p of familyPerms) {
|
|
const perm = await prisma.permission.findUnique({ where: { code: p } });
|
|
if (perm) {
|
|
await prisma.rolePermission.upsert({
|
|
where: { roleId_permissionId: { roleId: family.id, permissionId: perm.id } },
|
|
update: {},
|
|
create: { roleId: family.id, permissionId: perm.id },
|
|
});
|
|
}
|
|
}
|
|
|
|
// Authentik group mappings
|
|
const mappings = [
|
|
{ authGroup: 'RL-Owner', roleName: 'Owner' },
|
|
{ authGroup: 'RL-Employee', roleName: 'Employee' },
|
|
{ authGroup: 'RL-Family', roleName: 'Family' },
|
|
];
|
|
|
|
for (const m of mappings) {
|
|
const role = await prisma.role.findUnique({ where: { name: m.roleName } });
|
|
if (!role) continue;
|
|
// Ensure mapping exists (authGroup + roleId). AuthGroup can map to multiple roles.
|
|
const existing = await prisma.authGroupRoleMapping.findFirst({ where: { authGroup: m.authGroup, roleId: role.id } });
|
|
if (!existing) {
|
|
await prisma.authGroupRoleMapping.create({ data: { authGroup: m.authGroup, roleId: role.id } });
|
|
}
|
|
}
|
|
|
|
// Seed application catalog
|
|
const apps = [
|
|
{ code: 'core', name: 'Core', description: 'RayLab Core Dashboard', icon: 'mdi-apps', url: '/', applicationsClaim: 'core', displayOrder: 0 },
|
|
{ code: 'warung', name: 'Warung', description: 'Warung app', icon: 'mdi-store', url: '/warung', applicationsClaim: 'warung', displayOrder: 10 },
|
|
{ code: 'nextcloud', name: 'Nextcloud', description: 'Nextcloud', icon: 'mdi-cloud', url: 'https://nextcloud.example', applicationsClaim: 'nextcloud', displayOrder: 20 },
|
|
{ code: 'jellyfin', name: 'Jellyfin', description: 'Jellyfin media server', icon: 'mdi-movie', url: 'https://jellyfin.example', applicationsClaim: 'jellyfin', displayOrder: 30 },
|
|
{ code: 'forgejo', name: 'Forgejo', description: 'Forgejo git server', icon: 'mdi-source-repository', url: 'https://forgejo.example', applicationsClaim: 'forgejo', displayOrder: 40 },
|
|
{ code: 'immich', name: 'Immich', description: 'Immich photo server', icon: 'mdi-image', url: 'https://immich.example', applicationsClaim: 'immich', displayOrder: 50 },
|
|
{ code: 'grafana', name: 'Grafana', description: 'Grafana monitoring', icon: 'mdi-chart-line', url: 'https://grafana.example', applicationsClaim: 'grafana', displayOrder: 60 },
|
|
{ code: 'vaultwarden', name: 'Vaultwarden', description: 'Vaultwarden password manager', icon: 'mdi-lock', url: 'https://vaultwarden.example', applicationsClaim: 'vaultwarden', displayOrder: 70 },
|
|
{ code: 'homepage', name: 'Homepage', description: 'Homepage', icon: 'mdi-home', url: 'https://home.example', applicationsClaim: 'homepage', displayOrder: 80 },
|
|
];
|
|
|
|
for (const a of apps) {
|
|
await prisma.application.upsert({
|
|
where: { code: a.code },
|
|
update: { name: a.name, description: a.description, icon: a.icon, url: a.url, applicationsClaim: a.applicationsClaim, isActive: true, displayOrder: a.displayOrder },
|
|
create: { code: a.code, name: a.name, description: a.description, icon: a.icon, url: a.url, applicationsClaim: a.applicationsClaim, isActive: true, displayOrder: a.displayOrder },
|
|
});
|
|
}
|
|
|
|
console.log('Seeding completed.');
|
|
}
|
|
|
|
main()
|
|
.catch(async (e) => {
|
|
console.error(e);
|
|
await prisma.$disconnect();
|
|
process.exit(1);
|
|
})
|
|
.finally(async () => {
|
|
await prisma.$disconnect();
|
|
});
|
|
|
|
|