Files
lunarfront-app/packages/backend/src/db/schema/rbac.ts
Ryan Moon 4a1fc608f0 Implement RBAC with permissions, roles, and route guards
- permission, role, role_permission, user_role_assignment tables
- 42 system permissions across 13 domains
- 6 default roles: Admin, Manager, Sales Associate, Technician, Instructor, Viewer
- Permission inheritance: admin implies edit implies view
- requirePermission() Fastify decorator on ALL routes
- System permissions and roles seeded per company
- Test helpers and API test runner seed RBAC data
- All 42 API tests pass with permissions enforced
2026-03-28 17:00:42 -05:00

55 lines
2.1 KiB
TypeScript

import { pgTable, uuid, varchar, text, timestamp, boolean, uniqueIndex } from 'drizzle-orm/pg-core'
import { companies } from './stores.js'
import { users } from './users.js'
export const permissions = pgTable('permission', {
id: uuid('id').primaryKey().defaultRandom(),
slug: varchar('slug', { length: 100 }).notNull().unique(),
domain: varchar('domain', { length: 50 }).notNull(),
action: varchar('action', { length: 50 }).notNull(),
description: varchar('description', { length: 255 }).notNull(),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
})
export const roles = pgTable('role', {
id: uuid('id').primaryKey().defaultRandom(),
companyId: uuid('company_id')
.notNull()
.references(() => companies.id),
name: varchar('name', { length: 100 }).notNull(),
slug: varchar('slug', { length: 100 }).notNull(),
description: text('description'),
isSystem: boolean('is_system').notNull().default(false),
isActive: boolean('is_active').notNull().default(true),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
})
export const rolePermissions = pgTable('role_permission', {
id: uuid('id').primaryKey().defaultRandom(),
roleId: uuid('role_id')
.notNull()
.references(() => roles.id),
permissionId: uuid('permission_id')
.notNull()
.references(() => permissions.id),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
})
export const userRoles = pgTable('user_role_assignment', {
id: uuid('id').primaryKey().defaultRandom(),
userId: uuid('user_id')
.notNull()
.references(() => users.id),
roleId: uuid('role_id')
.notNull()
.references(() => roles.id),
assignedBy: uuid('assigned_by'),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
})
export type Permission = typeof permissions.$inferSelect
export type Role = typeof roles.$inferSelect
export type RolePermission = typeof rolePermissions.$inferSelect
export type UserRole = typeof userRoles.$inferSelect