Files
RayLab-Core/prisma/seed.ts
T
2026-08-02 17:44:50 +07:00

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();
});