Skip to content

Commit

Permalink
feat: scaffold acl structure
Browse files Browse the repository at this point in the history
  • Loading branch information
Nguyen Huu Nguyen Y committed Nov 17, 2024
1 parent 421a4da commit 7377efc
Show file tree
Hide file tree
Showing 16 changed files with 177 additions and 91 deletions.
13 changes: 0 additions & 13 deletions server/api/auth/signup.post.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { createHmac } from 'node:crypto'
import { Buffer } from 'node:buffer'
import bcrypt from 'bcrypt'
import { useRoleCrud } from '@base/server/composables/useRoleCrud'
import { useUserCrud } from '@base/server/composables/useUserCrud'
import { z } from 'zod'

Expand All @@ -17,17 +16,6 @@ export default defineEventHandler(async (event) => {
}).parse,
)

const { getRoleByName } = useRoleCrud()

const userRole = await getRoleByName('User')

if (!userRole.data?.id) {
throw createError({
statusCode: 403,
statusMessage: ErrorMessage.CANNOT_FIND_ROLE,
})
}

const { createUser } = useUserCrud()

const sysUser = await createUser({
Expand All @@ -41,7 +29,6 @@ export default defineEventHandler(async (event) => {
address: '',
organization: '',
provider,
role_id: userRole.data.id,
email_verified: provider === 'credentials' ? null : new Date(),
})

Expand Down
20 changes: 15 additions & 5 deletions server/composables/useRoleCrud.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { ParsedFilterQuery } from '@base/server/utils/filter'
import { sysRoleTable } from '@base/server/db/schemas'
import { eq } from 'drizzle-orm'
import { useCrud } from './useCrud'

export function useRoleCrud() {
const {
getRecordsPaginated,
getRecordByKey,
createRecord,
updateRecordByKey,
deleteRecordByKey,
Expand All @@ -21,15 +21,25 @@ export function useRoleCrud() {
}

async function getRoleById(id: string) {
const { data } = await getRecordByKey('id', id)
const data = await db.query.sysRoleTable.findFirst({
where: sysRoleTable => eq(sysRoleTable.id, id),
with: {
permissions: true,
},
})

return { data }
return data
}

async function getRoleByName(name: string) {
const { data } = await getRecordByKey('name', name)
const data = await db.query.sysRoleTable.findFirst({
where: sysRoleTable => eq(sysRoleTable.name, name),
with: {
permissions: true,
},
})

return { data }
return data
}

async function updateRoleById(id: string, body: any) {
Expand Down
18 changes: 16 additions & 2 deletions server/composables/useUserCrud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,23 @@ export function useUserCrud() {
password: false,
},
with: {
role: {
roles: {
with: {
permissions: true,
role: {
with: {
permissions: true,
},
},
},
},
organizations: {
with: {
organization: {
columns: {
id: true,
name: true,
},
},
},
},
},
Expand Down
9 changes: 9 additions & 0 deletions server/db/schemas/enum.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ function enumToPgEnum(myEnum: any): [string, ...string[]] {
return Object.values(myEnum).map((value: any) => `${value}`) as [string, ...string[]]
}

export enum PermissionScope {
ALL = 'all',
ORGANIZATION = 'organization',
SELF = 'self',
CUSTOM = 'custom',
}

export enum PermissionAction {
CREATE = 'create',
READ = 'read',
Expand All @@ -26,6 +33,8 @@ export enum PaymentStatus {

export const permissionAction = pgEnum('permission_action', enumToPgEnum(PermissionAction))

export const permissionScope = pgEnum('permission_scope', enumToPgEnum(PermissionScope))

export const userStatus = pgEnum('user_status', enumToPgEnum(UserStatus))

export const paymentStatus = pgEnum('payment_status', enumToPgEnum(PaymentStatus))
6 changes: 6 additions & 0 deletions server/db/schemas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,16 @@ export * from './sys_faqs.schema'

export * from './sys_notifications.schema'

export * from './sys_organization_user.schema'

export * from './sys_organizations.schema'

export * from './sys_permissions.schema'

export * from './sys_role_permission.schema'

export * from './sys_role_user.schema'

export * from './sys_roles.schema'

export * from './sys_users.schema'
Expand Down
22 changes: 22 additions & 0 deletions server/db/schemas/sys_organization_user.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core'
import { relations } from 'drizzle-orm/relations'
import { sysOrganizationTable } from './sys_organizations.schema'
import { sysUserTable } from './sys_users.schema'

export const sysOrganizationUserTable = pgTable('sys_organization_user', {
organization_id: uuid('organization_id').references(() => sysOrganizationTable.id, { onDelete: 'cascade' }).notNull(),
user_id: uuid('user_id').references(() => sysUserTable.id, { onDelete: 'cascade' }).notNull(),
}, table => ({
pk: primaryKey({ columns: [table.organization_id, table.user_id] }),
}))

export const sysOrganizationUserRelation = relations(sysOrganizationUserTable, ({ one }) => ({
organization: one(sysOrganizationTable, {
fields: [sysOrganizationUserTable.organization_id],
references: [sysOrganizationTable.id],
}),
user: one(sysUserTable, {
fields: [sysOrganizationUserTable.organization_id],
references: [sysUserTable.id],
}),
}))
4 changes: 2 additions & 2 deletions server/db/schemas/sys_organizations.schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { relations } from 'drizzle-orm/relations'
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
import { sysUserTable } from './sys_users.schema'
import { sysOrganizationUserTable } from './sys_organization_user.schema'

export const sysOrganizationTable = pgTable('sys_organizations', {
id: uuid('id').defaultRandom().primaryKey().notNull(),
Expand All @@ -11,5 +11,5 @@ export const sysOrganizationTable = pgTable('sys_organizations', {
})

export const sysOrganizationRelations = relations(sysOrganizationTable, ({ many }) => ({
users: many(sysUserTable),
users: many(sysOrganizationUserTable),
}))
16 changes: 7 additions & 9 deletions server/db/schemas/sys_permissions.schema.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
import { relations } from 'drizzle-orm/relations'
import { permissionAction } from './enum.schema'
import { sysRoleTable } from './sys_roles.schema'
import { PermissionAction, PermissionScope, permissionAction, permissionScope } from './enum.schema'
import { sysRolePermissionTable } from './sys_role_permission.schema'

export const sysPermissionTable = pgTable('sys_permissions', {
id: uuid('id').defaultRandom().primaryKey().notNull(),
role_id: uuid('role_id').references(() => sysRoleTable.id, { onDelete: 'cascade', onUpdate: 'cascade' }).notNull(),
action: permissionAction('action').default('read').notNull(),
action: permissionAction('action').default(PermissionAction.READ).notNull(),
subject: text('subject').notNull(),
scope: permissionScope('scope').default(PermissionScope.ALL),
scope_value: text('scope_value'),
})

export const sysPermissionRelations = relations(sysPermissionTable, ({ one }) => ({
role: one(sysRoleTable, {
fields: [sysPermissionTable.role_id],
references: [sysRoleTable.id],
}),
export const sysPermissionRelations = relations(sysPermissionTable, ({ many }) => ({
roles: many(sysRolePermissionTable),
}))
22 changes: 22 additions & 0 deletions server/db/schemas/sys_role_permission.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core'
import { relations } from 'drizzle-orm/relations'
import { sysRoleTable } from './sys_roles.schema'
import { sysPermissionTable } from './sys_permissions.schema'

export const sysRolePermissionTable = pgTable('sys_role_permission', {
role_id: uuid('role_id').references(() => sysRoleTable.id, { onDelete: 'cascade' }).notNull(),
permission_id: uuid('permission_id').references(() => sysPermissionTable.id, { onDelete: 'cascade' }).notNull(),
}, table => ({
pk: primaryKey({ columns: [table.role_id, table.permission_id] }),
}))

export const sysRolePermissionRelations = relations(sysRolePermissionTable, ({ one }) => ({
role: one(sysRoleTable, {
fields: [sysRolePermissionTable.role_id],
references: [sysRoleTable.id],
}),
permission: one(sysPermissionTable, {
fields: [sysRolePermissionTable.permission_id],
references: [sysPermissionTable.id],
}),
}))
22 changes: 22 additions & 0 deletions server/db/schemas/sys_role_user.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core'
import { relations } from 'drizzle-orm/relations'
import { sysRoleTable } from './sys_roles.schema'
import { sysUserTable } from './sys_users.schema'

export const sysRoleUserTable = pgTable('sys_role_user', {
role_id: uuid('role_id').references(() => sysRoleTable.id, { onDelete: 'cascade' }).notNull(),
user_id: uuid('user_id').references(() => sysUserTable.id, { onDelete: 'cascade' }).notNull(),
}, table => ({
pk: primaryKey({ columns: [table.role_id, table.user_id] }),
}))

export const sysRoleUserRelation = relations(sysRoleUserTable, ({ one }) => ({
role: one(sysRoleTable, {
fields: [sysRoleUserTable.role_id],
references: [sysRoleTable.id],
}),
user: one(sysUserTable, {
fields: [sysRoleUserTable.role_id],
references: [sysUserTable.id],
}),
}))
8 changes: 4 additions & 4 deletions server/db/schemas/sys_roles.schema.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { relations } from 'drizzle-orm/relations'
import { pgTable, text, uuid } from 'drizzle-orm/pg-core'
import { sysUserTable } from './sys_users.schema'
import { sysPermissionTable } from './sys_permissions.schema'
import { sysRolePermissionTable } from './sys_role_permission.schema'
import { sysRoleUserTable } from './sys_role_user.schema'

export const sysRoleTable = pgTable('sys_roles', {
id: uuid('id').defaultRandom().primaryKey().notNull(),
name: text('name').notNull(),
})

export const sysRoleRelations = relations(sysRoleTable, ({ many }) => ({
users: many(sysUserTable),
permissions: many(sysPermissionTable),
users: many(sysRoleUserTable),
permissions: many(sysRolePermissionTable),
}))
19 changes: 6 additions & 13 deletions server/db/schemas/sys_users.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { userDeviceTable } from './user_devices.schema'
import { userShortcutTable } from './user_shortcuts.schema'

import { userStatus } from './enum.schema'
import { sysRoleTable } from './sys_roles.schema'
import { sysOrganizationTable } from './sys_organizations.schema'
import { userOrderTable } from './user_orders.schema'
import { userPaymentTable } from './user_payments.schema'
import { sysOrganizationUserTable } from './sys_organization_user.schema'
import { sysRoleUserTable } from './sys_role_user.schema'

export const sysUserTable = pgTable('sys_users', {
id: uuid('id').defaultRandom().primaryKey().notNull(),
Expand All @@ -19,8 +19,6 @@ export const sysUserTable = pgTable('sys_users', {
avatar_url: text('avatar_url'),
created_at: timestamp('created_at', { withTimezone: true }).defaultNow(),
deleted_at: timestamp('deleted_at', { withTimezone: true }),
role_id: uuid('role_id').references(() => sysRoleTable.id, { onDelete: 'cascade', onUpdate: 'cascade' }),
organization_id: uuid('organization_id').references(() => sysOrganizationTable.id, { onDelete: 'cascade', onUpdate: 'cascade' }),
country: varchar('country'),
language: varchar('language').default('en'),
organization: text('organization'),
Expand All @@ -30,15 +28,10 @@ export const sysUserTable = pgTable('sys_users', {
city: text('city'),
})

export const sysUserRelations = relations(sysUserTable, ({ one, many }) => ({
role: one(sysRoleTable, {
fields: [sysUserTable.role_id],
references: [sysRoleTable.id],
}),
organization: one(sysOrganizationTable, {
fields: [sysUserTable.organization_id],
references: [sysOrganizationTable.id],
}),
export const sysUserRelations = relations(sysUserTable, ({ many }) => ({
roles: many(sysRoleUserTable),
organizations: many(sysOrganizationUserTable),

userShortcuts: many(userShortcutTable),
userDevices: many(userDeviceTable),
orders: many(userOrderTable),
Expand Down
26 changes: 18 additions & 8 deletions server/db/seeds/permissions.seed.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { InferSelectModel } from 'drizzle-orm'
import type { sysRoleTable } from '../schemas'
import { sysPermissionTable } from '../schemas'
import { sysPermissionTable, sysRolePermissionTable } from '../schemas'
import { db } from '../../utils/db'

export async function seedPermissions(roles: InferSelectModel<typeof sysRoleTable>[]) {
Expand All @@ -10,11 +10,21 @@ export async function seedPermissions(roles: InferSelectModel<typeof sysRoleTabl
const userRole = roles.find(role => role.name === 'User')
const visitorRole = roles.find(role => role.name === 'Visitor')

return await db.insert(sysPermissionTable).values([
{ role_id: adminRole!.id, action: 'manage' as const, subject: 'all' as const },
{ role_id: userRole!.id, action: 'manage' as const, subject: 'Project' as const },
{ role_id: visitorRole!.id, action: 'read' as const, subject: 'Project' as const },
{ role_id: userRole!.id, action: 'manage' as const, subject: 'Category' as const },
{ role_id: visitorRole!.id, action: 'read' as const, subject: 'Category' as const },
].filter(Boolean))
const data = await db.insert(sysPermissionTable).values([
{ action: 'manage' as const, subject: 'all' as const },
{ action: 'manage' as const, subject: 'Project' as const },
{ action: 'manage' as const, subject: 'Category' as const },
{ action: 'read' as const, subject: 'Project' as const },
{ action: 'read' as const, subject: 'Category' as const },
].filter(Boolean)).returning()

await db.insert(sysRolePermissionTable).values([
{ role_id: adminRole!.id, permission_id: data[0].id },
{ role_id: userRole!.id, permission_id: data[1].id },
{ role_id: userRole!.id, permission_id: data[2].id },
{ role_id: visitorRole!.id, permission_id: data[3].id },
{ role_id: visitorRole!.id, permission_id: data[4].id },
])

return data
}
16 changes: 10 additions & 6 deletions server/db/seeds/users.seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@ import { rand, randAvatar, randCountryCode, randEmail, randFullName, randLanguag
import type { InferSelectModel } from 'drizzle-orm'
import bcrypt from 'bcrypt'
import type { sysRoleTable } from '../schemas'
import { sysUserTable } from '../schemas'
import { sysRoleUserTable, sysUserTable } from '../schemas'
import { db } from '../../utils/db'

export async function seedUsers(roles: InferSelectModel<typeof sysRoleTable>[]) {
console.log('Seeding users...')

const editorRole = roles.find(role => role.name === 'User')
const userRole = roles.find(role => role.name === 'User')

const testUser = {
email: '[email protected]',
password: await bcrypt.hash('123456', 10),
role_id: editorRole!.id,
}

const users = await Promise.all(Array.from({ length: 15 }).fill(null).map(
Expand All @@ -29,13 +28,18 @@ export async function seedUsers(roles: InferSelectModel<typeof sysRoleTable>[])
postcode: '',
address: '',
organization: '',
role_id: rand(roles).id,
}),
))

users[0].email = testUser.email
users[0].password = testUser.password
users[0].role_id = testUser.role_id

return await db.insert(sysUserTable).values(users).returning()
const data = await db.insert(sysUserTable).values(users).returning()

await db.insert(sysRoleUserTable).values(data.map(user => ({
role_id: userRole!.id,
user_id: user.id,
})))

return data
}
Loading

0 comments on commit 7377efc

Please sign in to comment.