Refactor roles management to use role code as primary key

- Updated the roles table schema to use role code as the primary key, enhancing readability in junction tables.
- Modified related tables (user_roles, product_role_visibility) to reference role code instead of role ID.
- Adjusted seeding logic and utility functions to accommodate the new primary key structure, ensuring consistent role management across the application.
- Added a migration script to facilitate the database schema changes.
This commit is contained in:
Bastian Masanek
2025-11-03 10:45:11 +01:00
parent a6d09d76fd
commit 68ab42f2f4
5 changed files with 93 additions and 38 deletions

View File

@@ -160,10 +160,10 @@ export const products = pgTable(
* Roles Table
* Defines available user roles (private, educator, company)
* Phase 2/3: Educator and Company roles require approval workflow
* Note: Using role code as primary key for better readability in junction tables
*/
export const roles = pgTable('roles', {
id: uuid('id').primaryKey().defaultRandom(),
code: roleCodeEnum('code').unique().notNull(), // 'private', 'educator', 'company'
code: roleCodeEnum('code').primaryKey(), // 'private', 'educator', 'company' - Primary Key
displayName: text('display_name').notNull(), // "Privatperson", "Pädagoge", "Unternehmen"
description: text('description').notNull(), // Role description
requiresApproval: boolean('requires_approval').notNull().default(false), // false for 'private', true for 'educator'/'company'
@@ -186,9 +186,9 @@ export const userRoles = pgTable(
userId: uuid('user_id')
.notNull()
.references(() => users.id, { onDelete: 'cascade' }),
roleId: uuid('role_id')
roleCode: roleCodeEnum('role_code')
.notNull()
.references(() => roles.id, { onDelete: 'cascade' }),
.references(() => roles.code, { onDelete: 'cascade' }),
// Role request status (Phase 2/3 feature - prepared in MVP)
status: roleRequestStatusEnum('status').notNull().default('pending'),
@@ -206,8 +206,9 @@ export const userRoles = pgTable(
},
(table) => [
// Unique constraint: User can only have one entry per role
index('user_roles_user_id_role_id_unique').on(table.userId, table.roleId),
index('user_roles_user_id_role_code_unique').on(table.userId, table.roleCode),
index('user_roles_user_id_idx').on(table.userId),
index('user_roles_role_code_idx').on(table.roleCode),
index('user_roles_status_idx').on(table.status),
]
)
@@ -225,15 +226,16 @@ export const productRoleVisibility = pgTable(
productId: uuid('product_id')
.notNull()
.references(() => products.id, { onDelete: 'cascade' }),
roleId: uuid('role_id')
roleCode: roleCodeEnum('role_code')
.notNull()
.references(() => roles.id, { onDelete: 'cascade' }),
.references(() => roles.code, { onDelete: 'cascade' }),
createdAt: timestamp('created_at').defaultNow().notNull(),
},
(table) => [
// Unique constraint: Product-Role pair can only exist once
index('product_role_visibility_product_id_role_id_unique').on(table.productId, table.roleId),
index('product_role_visibility_product_id_role_code_unique').on(table.productId, table.roleCode),
index('product_role_visibility_product_id_idx').on(table.productId),
index('product_role_visibility_role_code_idx').on(table.roleCode),
]
)
@@ -381,8 +383,8 @@ export const userRolesRelations = relations(userRoles, ({ one }) => ({
references: [users.id],
}),
role: one(roles, {
fields: [userRoles.roleId],
references: [roles.id],
fields: [userRoles.roleCode],
references: [roles.code],
}),
}))
@@ -392,7 +394,7 @@ export const productRoleVisibilityRelations = relations(productRoleVisibility, (
references: [products.id],
}),
role: one(roles, {
fields: [productRoleVisibility.roleId],
references: [roles.id],
fields: [productRoleVisibility.roleCode],
references: [roles.code],
}),
}))