Init roles, permissions and resources repository

This commit is contained in:
vergiLgood1 2025-04-10 22:38:57 +07:00
parent a468f3db68
commit 258205ef49
74 changed files with 2375 additions and 38 deletions

View File

@ -0,0 +1,14 @@
import { useCheckPermissionsMutation } from "../_queries/mutations"
export const useCheckPermissionsHandler = () => {
const { mutateAsync: checkPermissions, isPending } = useCheckPermissionsMutation()
const handleCheckPermissions = async (userId: string, action: string, resource: string) => {
return await checkPermissions({ userId, action, resource })
}
return {
handleCheckPermissions,
isPending,
}
}

View File

@ -1,5 +1,5 @@
import { useMutation } from "@tanstack/react-query" import { useMutation } from "@tanstack/react-query"
import { sendMagicLink, sendPasswordRecovery, signInPasswordless, signInWithPassword, signOut, verifyOtp } from "../action" import { checkPermissions, sendMagicLink, sendPasswordRecovery, signInPasswordless, signInWithPassword, signOut, verifyOtp } from "../action"
export const useSignInPasswordlessMutation = () => { export const useSignInPasswordlessMutation = () => {
return useMutation({ return useMutation({
@ -42,3 +42,10 @@ export const useVerifyOtpMutation = () => {
mutationFn: async (formData: FormData) => await verifyOtp(formData), mutationFn: async (formData: FormData) => await verifyOtp(formData),
}) })
} }
export const useCheckPermissionsMutation = () => {
return useMutation({
mutationKey: ["checkPermissions"],
mutationFn: async ({ userId, action, resource }: { userId: string; action: string; resource: string }) => await checkPermissions(userId, action, resource),
})
}

View File

@ -227,3 +227,26 @@ export async function sendPasswordRecovery(email: string) {
} }
}) })
} }
export async function checkPermissions(userId: string, action: string, resource: string) {
const instrumentationService = getInjection("IInstrumentationService")
return await instrumentationService.instrumentServerAction("checkPermissions", {
recordResponse: true
}, async () => {
try {
const checkPermissionsController = getInjection("ICheckPermissionsController")
return await checkPermissionsController({ userId, action, resource })
} catch (err) {
if (err instanceof InputParseError) {
return { error: err.message }
}
const crashReporterService = getInjection("ICrashReporterService")
crashReporterService.report(err)
return {
error: "An error occurred during permissions check. Please try again later.",
}
}
})
}

View File

@ -5,6 +5,9 @@ import { createAuthenticationModule } from './modules/authentication.module';
import { createMonitoringModule } from './modules/monitoring.module'; import { createMonitoringModule } from './modules/monitoring.module';
import { createTransactionManagerModule } from './modules/database.modul'; import { createTransactionManagerModule } from './modules/database.modul';
import { createUsersModule } from './modules/users.module'; import { createUsersModule } from './modules/users.module';
import { createRolesModule } from './modules/roles.module';
import { createPermissionsModule } from './modules/permissions.module';
import { createResourcesModule } from './modules/resources.module';
const ApplicationContainer = createContainer(); const ApplicationContainer = createContainer();
@ -12,6 +15,9 @@ ApplicationContainer.load(Symbol('MonitoringModule'), createMonitoringModule());
ApplicationContainer.load(Symbol('TransactionManagerModule'), createTransactionManagerModule()); ApplicationContainer.load(Symbol('TransactionManagerModule'), createTransactionManagerModule());
ApplicationContainer.load(Symbol('AuthenticationModule'), createAuthenticationModule()); ApplicationContainer.load(Symbol('AuthenticationModule'), createAuthenticationModule());
ApplicationContainer.load(Symbol('UsersModule'), createUsersModule()); ApplicationContainer.load(Symbol('UsersModule'), createUsersModule());
ApplicationContainer.load(Symbol('RolesModule'), createRolesModule());
ApplicationContainer.load(Symbol('PermissionsModule'), createPermissionsModule());
ApplicationContainer.load(Symbol('ResourcesModule'), createResourcesModule());
export function getInjection<K extends keyof typeof DI_SYMBOLS>( export function getInjection<K extends keyof typeof DI_SYMBOLS>(
symbol: K symbol: K

View File

@ -0,0 +1,107 @@
import { createModule } from '@evyweb/ioctopus';
import { DI_SYMBOLS } from '@/di/types';
import { createPermissionUseCase } from '@/src/application/use-cases/permissions/create-permissions.use-case';
import { getAllPermissionsUseCase } from '@/src/application/use-cases/permissions/get-all-permissions';
import { getPermissionByIdUseCase } from '@/src/application/use-cases/permissions/get-permissions-by-id.use-case';
import { getPermissionByRoleUseCase } from '@/src/application/use-cases/permissions/get-permissions-by-role.use-case';
import { updatePermissionUseCase } from '@/src/application/use-cases/permissions/update-permissions.use-case';
import { deletePermissionUseCase } from '@/src/application/use-cases/permissions/delete-permissions.use-case';
import { createPermissionController } from '@/src/interface-adapters/controllers/permissions/create-permission.controller';
import { getAllPermissionsController } from '@/src/interface-adapters/controllers/permissions/get-all-permission.controller';
import { getPermissionByIdController } from '@/src/interface-adapters/controllers/permissions/get-permission-by-id.controller';
import { getPermissionByRoleController } from '@/src/interface-adapters/controllers/permissions/get-permission-by-role.controller';
import { updatePermissionController } from '@/src/interface-adapters/controllers/permissions/update-permission.controller';
import { deletePermissionController } from '@/src/interface-adapters/controllers/permissions/delete-permission.controller';
export function createPermissionsModule() {
const permissionsModule = createModule();
// Use Cases
permissionsModule
.bind(DI_SYMBOLS.ICreatePermissionUseCase)
.toHigherOrderFunction(createPermissionUseCase, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IPermissionsRepository,
]);
permissionsModule
.bind(DI_SYMBOLS.IGetAllPermissionsUseCase)
.toHigherOrderFunction(getAllPermissionsUseCase, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IPermissionsRepository,
]);
permissionsModule
.bind(DI_SYMBOLS.IGetPermissionByIdUseCase)
.toHigherOrderFunction(getPermissionByIdUseCase, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IPermissionsRepository,
]);
permissionsModule
.bind(DI_SYMBOLS.IGetPermissionByRoleUseCase)
.toHigherOrderFunction(getPermissionByRoleUseCase, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IPermissionsRepository,
]);
permissionsModule
.bind(DI_SYMBOLS.IUpdatePermissionUseCase)
.toHigherOrderFunction(updatePermissionUseCase, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IPermissionsRepository,
]);
permissionsModule
.bind(DI_SYMBOLS.IDeletePermissionUseCase)
.toHigherOrderFunction(deletePermissionUseCase, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IPermissionsRepository,
]);
// Controllers
permissionsModule
.bind(DI_SYMBOLS.ICreatePermissionController)
.toHigherOrderFunction(createPermissionController, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.ICreatePermissionUseCase,
]);
permissionsModule
.bind(DI_SYMBOLS.IGetAllPermissionsController)
.toHigherOrderFunction(getAllPermissionsController, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IGetAllPermissionsUseCase,
]);
permissionsModule
.bind(DI_SYMBOLS.IGetPermissionByIdController)
.toHigherOrderFunction(getPermissionByIdController, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IGetPermissionByIdUseCase,
]);
permissionsModule
.bind(DI_SYMBOLS.IGetPermissionByRoleController)
.toHigherOrderFunction(getPermissionByRoleController, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IGetPermissionByRoleUseCase,
]);
permissionsModule
.bind(DI_SYMBOLS.IUpdatePermissionController)
.toHigherOrderFunction(updatePermissionController, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IUpdatePermissionUseCase,
]);
permissionsModule
.bind(DI_SYMBOLS.IDeletePermissionController)
.toHigherOrderFunction(deletePermissionController, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IDeletePermissionUseCase,
]);
return permissionsModule;
}

View File

@ -0,0 +1,91 @@
import { createModule } from '@evyweb/ioctopus';
import { DI_SYMBOLS } from '@/di/types';
import { createResourceUseCase } from '@/src/application/use-cases/resources/create-resources.use-case';
import { getResourceByIdUseCase } from '@/src/application/use-cases/resources/get-resource-by-id.use-case';
import { getResourcesByTypeUseCase } from '@/src/application/use-cases/resources/get-resources-by-type.use-case';
import { updateResourceUseCase } from '@/src/application/use-cases/resources/update-resource.use-case';
import { deleteResourceUseCase } from '@/src/application/use-cases/resources/delete-resource.use-case';
import { createResourceController } from '@/src/interface-adapters/controllers/resources/create-resource.controller';
import { getResourceByIdController } from '@/src/interface-adapters/controllers/resources/get-resource-by-id.controller';
import { getResourcesByTypeController } from '@/src/interface-adapters/controllers/resources/get-resources-by-type.controller';
import { updateResourceController } from '@/src/interface-adapters/controllers/resources/update-resource.controller';
import { deleteResourceController } from '@/src/interface-adapters/controllers/resources/delete-resource.controller';
export function createResourcesModule() {
const resourcesModule = createModule();
// Use Cases
resourcesModule
.bind(DI_SYMBOLS.ICreateResourceUseCase)
.toHigherOrderFunction(createResourceUseCase, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IResourcesRepository,
]);
resourcesModule
.bind(DI_SYMBOLS.IGetResourceByIdUseCase)
.toHigherOrderFunction(getResourceByIdUseCase, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IResourcesRepository,
]);
resourcesModule
.bind(DI_SYMBOLS.IGetResourcesByTypeUseCase)
.toHigherOrderFunction(getResourcesByTypeUseCase, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IResourcesRepository,
]);
resourcesModule
.bind(DI_SYMBOLS.IUpdateResourceUseCase)
.toHigherOrderFunction(updateResourceUseCase, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IResourcesRepository,
]);
resourcesModule
.bind(DI_SYMBOLS.IDeleteResourceUseCase)
.toHigherOrderFunction(deleteResourceUseCase, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IResourcesRepository,
]);
// Controllers
resourcesModule
.bind(DI_SYMBOLS.ICreateResourceController)
.toHigherOrderFunction(createResourceController, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.ICreateResourceUseCase,
]);
resourcesModule
.bind(DI_SYMBOLS.IGetResourceByIdController)
.toHigherOrderFunction(getResourceByIdController, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IGetResourceByIdUseCase,
]);
resourcesModule
.bind(DI_SYMBOLS.IGetResourcesByTypeController)
.toHigherOrderFunction(getResourcesByTypeController, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IGetResourcesByTypeUseCase,
]);
resourcesModule
.bind(DI_SYMBOLS.IUpdateResourceController)
.toHigherOrderFunction(updateResourceController, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IUpdateResourceUseCase,
]);
resourcesModule
.bind(DI_SYMBOLS.IDeleteResourceController)
.toHigherOrderFunction(deleteResourceController, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IDeleteResourceUseCase,
]);
return resourcesModule;
}

View File

@ -0,0 +1,75 @@
import { createModule } from '@evyweb/ioctopus';
import { DI_SYMBOLS } from '@/di/types';
import { createRoleUseCase } from '@/src/application/use-cases/roles/create-role.use-case';
import { getRoleByIdUseCase } from '@/src/application/use-cases/roles/get-role-by-id.use-case';
import { updateRoleUseCase } from '@/src/application/use-cases/roles/update-role.use-case';
import { deleteRoleUseCase } from '@/src/application/use-cases/roles/delete-role.use-case';
import { createRoleController } from '@/src/interface-adapters/controllers/roles/create-role.controller';
import { getRoleByIdController } from '@/src/interface-adapters/controllers/roles/get-role-by-id.controller';
import { updateRoleController } from '@/src/interface-adapters/controllers/roles/update-role.controller';
import { deleteRoleController } from '@/src/interface-adapters/controllers/roles/delete-role.controller';
export function createRolesModule() {
const rolesModule = createModule();
// Use Cases
rolesModule
.bind(DI_SYMBOLS.ICreateRoleUseCase)
.toHigherOrderFunction(createRoleUseCase, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IRolesRepository,
]);
rolesModule
.bind(DI_SYMBOLS.IGetRoleByIdUseCase)
.toHigherOrderFunction(getRoleByIdUseCase, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IRolesRepository,
]);
rolesModule
.bind(DI_SYMBOLS.IUpdateRoleUseCase)
.toHigherOrderFunction(updateRoleUseCase, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IRolesRepository,
]);
rolesModule
.bind(DI_SYMBOLS.IDeleteRoleUseCase)
.toHigherOrderFunction(deleteRoleUseCase, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IRolesRepository,
]);
// Controllers
rolesModule
.bind(DI_SYMBOLS.ICreateRoleController)
.toHigherOrderFunction(createRoleController, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.ICreateRoleUseCase,
]);
rolesModule
.bind(DI_SYMBOLS.IGetRoleByIdController)
.toHigherOrderFunction(getRoleByIdController, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IGetRoleByIdUseCase,
]);
rolesModule
.bind(DI_SYMBOLS.IUpdateRoleController)
.toHigherOrderFunction(updateRoleController, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IUpdateRoleUseCase,
]);
rolesModule
.bind(DI_SYMBOLS.IDeleteRoleController)
.toHigherOrderFunction(deleteRoleController, [
DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IDeleteRoleUseCase,
]);
return rolesModule;
}

View File

@ -90,7 +90,7 @@ export function createUsersModule() {
]); ]);
usersModule usersModule
.bind(DI_SYMBOLS.IGetUserByUserNameUseCase) .bind(DI_SYMBOLS.IGetUserByUsernameUseCase)
.toHigherOrderFunction(getUserByUsernameUseCase, [ .toHigherOrderFunction(getUserByUsernameUseCase, [
DI_SYMBOLS.IInstrumentationService, DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IUsersRepository DI_SYMBOLS.IUsersRepository
@ -180,7 +180,7 @@ export function createUsersModule() {
.bind(DI_SYMBOLS.IGetUserByUsernameController) .bind(DI_SYMBOLS.IGetUserByUsernameController)
.toHigherOrderFunction(getUserByUsernameController, [ .toHigherOrderFunction(getUserByUsernameController, [
DI_SYMBOLS.IInstrumentationService, DI_SYMBOLS.IInstrumentationService,
DI_SYMBOLS.IGetUserByUserNameUseCase DI_SYMBOLS.IGetUserByUsernameUseCase
]); ]);
usersModule usersModule

View File

@ -40,8 +40,46 @@ import { ISendPasswordRecoveryController } from '@/src/interface-adapters/contro
import { IUploadAvatarController } from '@/src/interface-adapters/controllers/users/upload-avatar.controller'; import { IUploadAvatarController } from '@/src/interface-adapters/controllers/users/upload-avatar.controller';
import { IUploadAvatarUseCase } from '@/src/application/use-cases/users/upload-avatar.use-case'; import { IUploadAvatarUseCase } from '@/src/application/use-cases/users/upload-avatar.use-case';
import { ISignInWithPasswordController } from '@/src/interface-adapters/controllers/auth/sign-in-with-password.controller'; import { ISignInWithPasswordController } from '@/src/interface-adapters/controllers/auth/sign-in-with-password.controller';
import { IRolesRepository } from '@/src/application/repositories/roles.repository.interface';
import { IPermissionsRepository } from '@/src/application/repositories/permissions.repository.interface';
import { IResourcesRepository } from '@/src/application/repositories/resources.repository.interface';
import { ICreateRoleUseCase } from '@/src/application/use-cases/roles/create-role.use-case';
import { IGetRoleByIdUseCase } from '@/src/application/use-cases/roles/get-role-by-id.use-case';
import { IUpdateRoleUseCase } from '@/src/application/use-cases/roles/update-role.use-case';
import { IDeleteRoleUseCase } from '@/src/application/use-cases/roles/delete-role.use-case';
import { ICreatePermissionUseCase } from '@/src/application/use-cases/permissions/create-permissions.use-case';
import { IGetAllPermissionsUseCase } from '@/src/application/use-cases/permissions/get-all-permissions';
import { IGetPermissionByIdUseCase } from '@/src/application/use-cases/permissions/get-permissions-by-id.use-case';
import { IGetPermissionByRoleUseCase } from '@/src/application/use-cases/permissions/get-permissions-by-role.use-case';
import { IGetPermissionByRoleAndResourceController } from '@/src/interface-adapters/controllers/permissions/get-permission-by-role-and-resource.controller';
import { IGetPermissionByRoleAndResourcesUseCase } from '@/src/application/use-cases/permissions/get-permissions-by-role-and-resources.use-case';
import { IUpdatePermissionUseCase } from '@/src/application/use-cases/permissions/update-permissions.use-case';
import { IDeletePermissionUseCase } from '@/src/application/use-cases/permissions/delete-permissions.use-case';
import { ICreateResourceUseCase } from '@/src/application/use-cases/resources/create-resources.use-case';
import { IGetResourceByIdUseCase } from '@/src/application/use-cases/resources/get-resource-by-id.use-case';
import { IGetResourcesByTypeUseCase } from '@/src/application/use-cases/resources/get-resources-by-type.use-case';
import { IUpdateResourceUseCase } from '@/src/application/use-cases/resources/update-resource.use-case';
import { IDeleteResourceUseCase } from '@/src/application/use-cases/resources/delete-resource.use-case';
import { ICheckPermissionsController } from '@/src/interface-adapters/controllers/auth/check-permissions.controller';
import { ICreateRoleController } from '@/src/interface-adapters/controllers/roles/create-role.controller';
import { IGetRoleByIdController } from '@/src/interface-adapters/controllers/roles/get-role-by-id.controller';
import { IUpdateRoleController } from '@/src/interface-adapters/controllers/roles/update-role.controller';
import { IDeleteRoleController } from '@/src/interface-adapters/controllers/roles/delete-role.controller';
import { ICreatePermissionController } from '@/src/interface-adapters/controllers/permissions/create-permission.controller';
import { IGetAllPermissionsController } from '@/src/interface-adapters/controllers/permissions/get-all-permission.controller';
import { IGetPermissionByIdController } from '@/src/interface-adapters/controllers/permissions/get-permission-by-id.controller';
import { IGetPermissionByRoleController } from '@/src/interface-adapters/controllers/permissions/get-permission-by-role.controller';
import { IUpdatePermissionController } from '@/src/interface-adapters/controllers/permissions/update-permission.controller';
import { IDeletePermissionController } from '@/src/interface-adapters/controllers/permissions/delete-permission.controller';
import { IGetResourceByIdController } from '@/src/interface-adapters/controllers/resources/get-resource-by-id.controller';
import { IGetResourcesByTypeController } from '@/src/interface-adapters/controllers/resources/get-resources-by-type.controller';
import { IDeleteResourceController } from '@/src/interface-adapters/controllers/resources/delete-resource.controller';
import { IUpdateResourceController } from '@/src/interface-adapters/controllers/resources/update-resource.controller';
import { ICreateResourceController } from '@/src/interface-adapters/controllers/resources/create-resource.controller';
import { ICheckPermissionsUseCase } from '@/src/application/use-cases/auth/check-permissions.use-case';
export const DI_SYMBOLS = { // Pastikan DI_SYMBOLS memiliki tipe yang sesuai dengan DI_RETURN_TYPES
export const DI_SYMBOLS: { [K in keyof DI_RETURN_TYPES]: symbol } = {
// Services // Services
IAuthenticationService: Symbol.for('IAuthenticationService'), IAuthenticationService: Symbol.for('IAuthenticationService'),
ITransactionManagerService: Symbol.for('ITransactionManagerService'), ITransactionManagerService: Symbol.for('ITransactionManagerService'),
@ -50,37 +88,69 @@ export const DI_SYMBOLS = {
// Repositories // Repositories
IUsersRepository: Symbol.for('IUsersRepository'), IUsersRepository: Symbol.for('IUsersRepository'),
IRolesRepository: Symbol.for('IRolesRepository'),
IPermissionsRepository: Symbol.for('IPermissionsRepository'),
IResourcesRepository: Symbol.for('IResourcesRepository'),
// Use Cases // Use Cases
// Auth Use Cases
ISignInPasswordlessUseCase: Symbol.for('ISignInPasswordlessUseCase'), ISignInPasswordlessUseCase: Symbol.for('ISignInPasswordlessUseCase'),
ISignInWithPasswordUseCase: Symbol.for('ISignInWithPasswordUseCase'),
ISignUpUseCase: Symbol.for('ISignUpUseCase'), ISignUpUseCase: Symbol.for('ISignUpUseCase'),
IVerifyOtpUseCase: Symbol.for('IVerifyOtpUseCase'), IVerifyOtpUseCase: Symbol.for('IVerifyOtpUseCase'),
ISignOutUseCase: Symbol.for('ISignOutUseCase'), ISignOutUseCase: Symbol.for('ISignOutUseCase'),
ISendMagicLinkUseCase: Symbol.for('ISendMagicLinkUseCase'), ISendMagicLinkUseCase: Symbol.for('ISendMagicLinkUseCase'),
ISendPasswordRecoveryUseCase: Symbol.for('ISendPasswordRecoveryUseCase'), ISendPasswordRecoveryUseCase: Symbol.for('ISendPasswordRecoveryUseCase'),
ICheckPermissionsUseCase: Symbol.for('ICheckPermissionsUseCase'),
// User Use Cases
IBanUserUseCase: Symbol.for('IBanUserUseCase'), IBanUserUseCase: Symbol.for('IBanUserUseCase'),
IUnbanUserUseCase: Symbol.for('IUnbanUserUseCase'), IUnbanUserUseCase: Symbol.for('IUnbanUserUseCase'),
IGetCurrentUserUseCase: Symbol.for('IGetCurrentUserUseCase'), IGetCurrentUserUseCase: Symbol.for('IGetCurrentUserUseCase'),
IGetUsersUseCase: Symbol.for('IGetUsersUseCase'), IGetUsersUseCase: Symbol.for('IGetUsersUseCase'),
IGetUserByIdUseCase: Symbol.for('IGetUserByIdUseCase'), IGetUserByIdUseCase: Symbol.for('IGetUserByIdUseCase'),
IGetUserByEmailUseCase: Symbol.for('IGetUserByEmailUseCase'), IGetUserByEmailUseCase: Symbol.for('IGetUserByEmailUseCase'),
IGetUserByUserNameUseCase: Symbol.for('IGetUserByUserNameUseCase'), IGetUserByUsernameUseCase: Symbol.for('IGetUserByUsernameUseCase'),
IInviteUserUseCase: Symbol.for('IInviteUserUseCase'), IInviteUserUseCase: Symbol.for('IInviteUserUseCase'),
ICreateUserUseCase: Symbol.for('ICreateUserUseCase'), ICreateUserUseCase: Symbol.for('ICreateUserUseCase'),
IUpdateUserUseCase: Symbol.for('IUpdateUserUseCase'), IUpdateUserUseCase: Symbol.for('IUpdateUserUseCase'),
IDeleteUserUseCase: Symbol.for('IDeleteUserUseCase'), IDeleteUserUseCase: Symbol.for('IDeleteUserUseCase'),
IUploadAvatarUseCase: Symbol.for('IUploadAvatarUseCase'), IUploadAvatarUseCase: Symbol.for('IUploadAvatarUseCase'),
// Role Use Cases
ICreateRoleUseCase: Symbol.for('ICreateRoleUseCase'),
IGetRoleByIdUseCase: Symbol.for('IGetRoleByIdUseCase'),
IUpdateRoleUseCase: Symbol.for('IUpdateRoleUseCase'),
IDeleteRoleUseCase: Symbol.for('IDeleteRoleUseCase'),
// Permission Use Cases
ICreatePermissionUseCase: Symbol.for('ICreatePermissionUseCase'),
IGetAllPermissionsUseCase: Symbol.for('IGetAllPermissionsUseCase'),
IGetPermissionByIdUseCase: Symbol.for('IGetPermissionByIdUseCase'),
IGetPermissionByRoleUseCase: Symbol.for('IGetPermissionByRoleUseCase'),
IGetPermissionByRoleAndResourcesUseCase: Symbol.for('IGetPermissionByRoleAndResourcesUseCase'),
IUpdatePermissionUseCase: Symbol.for('IUpdatePermissionUseCase'),
IDeletePermissionUseCase: Symbol.for('IDeletePermissionUseCase'),
// Resource Use Cases
ICreateResourceUseCase: Symbol.for('ICreateResourceUseCase'),
IGetResourceByIdUseCase: Symbol.for('IGetResourceByIdUseCase'),
IGetResourcesByTypeUseCase: Symbol.for('IGetResourcesByTypeUseCase'),
IUpdateResourceUseCase: Symbol.for('IUpdateResourceUseCase'),
IDeleteResourceUseCase: Symbol.for('IDeleteResourceUseCase'),
// Controllers // Controllers
// Auth Controllers
ISignInPasswordlessController: Symbol.for('ISignInPasswordlessController'), ISignInPasswordlessController: Symbol.for('ISignInPasswordlessController'),
ISignInWithPasswordController: Symbol.for('ISignInWithPasswordController'), ISignInWithPasswordController: Symbol.for('ISignInWithPasswordController'),
ISignOutController: Symbol.for('ISignOutController'), ISignOutController: Symbol.for('ISignOutController'),
IVerifyOtpController: Symbol.for('IVerifyOtpController'), IVerifyOtpController: Symbol.for('IVerifyOtpController'),
ISendMagicLinkController: Symbol.for('ISendMagicLinkController'), ISendMagicLinkController: Symbol.for('ISendMagicLinkController'),
ISendPasswordRecoveryController: Symbol.for('ISendPasswordRecoveryController'), ISendPasswordRecoveryController: Symbol.for('ISendPasswordRecoveryController'),
ICheckPermissionsController: Symbol.for('ICheckPermissionsController'),
// User Controllers
IBanUserController: Symbol.for('IBanUserController'), IBanUserController: Symbol.for('IBanUserController'),
IUnbanUserController: Symbol.for('IUnbanUserController'), IUnbanUserController: Symbol.for('IUnbanUserController'),
IGetCurrentUserController: Symbol.for('IGetCurrentUserController'), IGetCurrentUserController: Symbol.for('IGetCurrentUserController'),
@ -93,6 +163,28 @@ export const DI_SYMBOLS = {
IUpdateUserController: Symbol.for('IUpdateUserController'), IUpdateUserController: Symbol.for('IUpdateUserController'),
IDeleteUserController: Symbol.for('IDeleteUserController'), IDeleteUserController: Symbol.for('IDeleteUserController'),
IUploadAvatarController: Symbol.for('IUploadAvatarController'), IUploadAvatarController: Symbol.for('IUploadAvatarController'),
// Role Controllers
ICreateRoleController: Symbol.for('ICreateRoleController'),
IGetRoleByIdController: Symbol.for('IGetRoleByIdController'),
IUpdateRoleController: Symbol.for('IUpdateRoleController'),
IDeleteRoleController: Symbol.for('IDeleteRoleController'),
// Permission Controllers
ICreatePermissionController: Symbol.for('ICreatePermissionController'),
IGetAllPermissionsController: Symbol.for('IGetAllPermissionsController'),
IGetPermissionByIdController: Symbol.for('IGetPermissionByIdController'),
IGetPermissionByRoleController: Symbol.for('IGetPermissionByRoleController'),
IGetPermissionByRoleAndResourceController: Symbol.for('IGetPermissionByRoleAndResourceController'),
IUpdatePermissionController: Symbol.for('IUpdatePermissionController'),
IDeletePermissionController: Symbol.for('IDeletePermissionController'),
// Resource Controllers
ICreateResourceController: Symbol.for('ICreateResourceController'),
IGetResourceByIdController: Symbol.for('IGetResourceByIdController'),
IGetResourcesByTypeController: Symbol.for('IGetResourcesByTypeController'),
IUpdateResourceController: Symbol.for('IUpdateResourceController'),
IDeleteResourceController: Symbol.for('IDeleteResourceController'),
}; };
export interface DI_RETURN_TYPES { export interface DI_RETURN_TYPES {
@ -104,36 +196,69 @@ export interface DI_RETURN_TYPES {
// Repositories // Repositories
IUsersRepository: IUsersRepository; IUsersRepository: IUsersRepository;
IRolesRepository: IRolesRepository;
IPermissionsRepository: IPermissionsRepository;
IResourcesRepository: IResourcesRepository;
// Use Cases // Use Cases
// Auth Use Cases
ISignInPasswordlessUseCase: ISignInPasswordlessUseCase; ISignInPasswordlessUseCase: ISignInPasswordlessUseCase;
ISignUpUseCase: ISignUpUseCase; ISignUpUseCase: ISignUpUseCase;
IVerifyOtpUseCase: IVerifyOtpUseCase; IVerifyOtpUseCase: IVerifyOtpUseCase;
ISignOutUseCase: ISignOutUseCase; ISignOutUseCase: ISignOutUseCase;
ISendMagicLinkUseCase: ISendMagicLinkUseCase; ISendMagicLinkUseCase: ISendMagicLinkUseCase;
ISendPasswordRecoveryUseCase: ISendPasswordRecoveryUseCase; ISendPasswordRecoveryUseCase: ISendPasswordRecoveryUseCase;
ICheckPermissionsUseCase: ICheckPermissionsUseCase;
// User Use Cases
IBanUserUseCase: IBanUserUseCase; IBanUserUseCase: IBanUserUseCase;
IUnbanUserUseCase: IUnbanUserUseCase; IUnbanUserUseCase: IUnbanUserUseCase;
IGetCurrentUserUseCase: IGetCurrentUserUseCase; IGetCurrentUserUseCase: IGetCurrentUserUseCase;
IGetUsersUseCase: IGetUsersUseCase; IGetUsersUseCase: IGetUsersUseCase;
IGetUserByIdUseCase: IGetUserByIdUseCase; IGetUserByIdUseCase: IGetUserByIdUseCase;
IGetUserByEmailUseCase: IGetUserByEmailUseCase; IGetUserByEmailUseCase: IGetUserByEmailUseCase;
IGetUserByUserNameUseCase: IGetUserByUsernameUseCase; IGetUserByUsernameUseCase: IGetUserByUsernameUseCase;
IInviteUserUseCase: IInviteUserUseCase; IInviteUserUseCase: IInviteUserUseCase;
ICreateUserUseCase: ICreateUserUseCase; ICreateUserUseCase: ICreateUserUseCase;
IUpdateUserUseCase: IUpdateUserUseCase; IUpdateUserUseCase: IUpdateUserUseCase;
IDeleteUserUseCase: IDeleteUserUseCase; IDeleteUserUseCase: IDeleteUserUseCase;
IUploadAvatarUseCase: IUploadAvatarUseCase; IUploadAvatarUseCase: IUploadAvatarUseCase;
// Role Use Cases
ICreateRoleUseCase: ICreateRoleUseCase;
IGetRoleByIdUseCase: IGetRoleByIdUseCase;
IUpdateRoleUseCase: IUpdateRoleUseCase;
IDeleteRoleUseCase: IDeleteRoleUseCase;
// Permission Use Cases
ICreatePermissionUseCase: ICreatePermissionUseCase;
IGetAllPermissionsUseCase: IGetAllPermissionsUseCase;
IGetPermissionByIdUseCase: IGetPermissionByIdUseCase;
IGetPermissionByRoleUseCase: IGetPermissionByRoleUseCase;
IGetPermissionByRoleAndResourcesUseCase: IGetPermissionByRoleAndResourcesUseCase;
IUpdatePermissionUseCase: IUpdatePermissionUseCase;
IDeletePermissionUseCase: IDeletePermissionUseCase;
// Resource Use Cases
ICreateResourceUseCase: ICreateResourceUseCase;
IGetResourceByIdUseCase: IGetResourceByIdUseCase;
IGetResourcesByTypeUseCase: IGetResourcesByTypeUseCase;
IUpdateResourceUseCase: IUpdateResourceUseCase;
IDeleteResourceUseCase: IDeleteResourceUseCase;
// Controllers // Controllers
// Auth Controllers
ISignInPasswordlessController: ISignInPasswordlessController; ISignInPasswordlessController: ISignInPasswordlessController;
ISignInWithPasswordController: ISignInWithPasswordController; ISignInWithPasswordController: ISignInWithPasswordController;
IVerifyOtpController: IVerifyOtpController; IVerifyOtpController: IVerifyOtpController;
ISignOutController: ISignOutController; ISignOutController: ISignOutController;
ISendMagicLinkController: ISendMagicLinkController; ISendMagicLinkController: ISendMagicLinkController;
ISendPasswordRecoveryController: ISendPasswordRecoveryController; ISendPasswordRecoveryController: ISendPasswordRecoveryController;
ICheckPermissionsController: ICheckPermissionsController;
// User Controllers
IBanUserController: IBanUserController; IBanUserController: IBanUserController;
IUnbanUserController: IUnbanUserController; IUnbanUserController: IUnbanUserController;
IGetCurrentUserController: IGetCurrentUserController; IGetCurrentUserController: IGetCurrentUserController;
@ -147,4 +272,26 @@ export interface DI_RETURN_TYPES {
IDeleteUserController: IDeleteUserController; IDeleteUserController: IDeleteUserController;
IUploadAvatarController: IUploadAvatarController; IUploadAvatarController: IUploadAvatarController;
// Role Controllers
ICreateRoleController: ICreateRoleController;
IGetRoleByIdController: IGetRoleByIdController;
IUpdateRoleController: IUpdateRoleController;
IDeleteRoleController: IDeleteRoleController;
// Permission Controllers
ICreatePermissionController: ICreatePermissionController;
IGetAllPermissionsController: IGetAllPermissionsController;
IGetPermissionByIdController: IGetPermissionByIdController;
IGetPermissionByRoleController: IGetPermissionByRoleController;
IGetPermissionByRoleAndResourceController: IGetPermissionByRoleAndResourceController;
IUpdatePermissionController: IUpdatePermissionController;
IDeletePermissionController: IDeletePermissionController;
// Resource Controllers
ICreateResourceController: ICreateResourceController;
IGetResourceByIdController: IGetResourceByIdController;
IGetResourcesByTypeController: IGetResourcesByTypeController;
IUpdateResourceController: IUpdateResourceController;
IDeleteResourceController: IDeleteResourceController;
} }

View File

@ -7,7 +7,7 @@
"db:seed": "npx prisma db seed" "db:seed": "npx prisma db seed"
}, },
"prisma": { "prisma": {
"seed": "ts-node prisma/seed.ts" "seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts"
}, },
"dependencies": { "dependencies": {
"@evyweb/ioctopus": "^1.2.0", "@evyweb/ioctopus": "^1.2.0",

View File

@ -183,6 +183,7 @@ model roles {
model resources { model resources {
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
name String @unique @db.VarChar(255) name String @unique @db.VarChar(255)
type String?
description String? description String?
instance_role String? instance_role String?
relations String? relations String?
@ -195,8 +196,8 @@ model resources {
model permissions { model permissions {
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
action String action String
resource_id String resource_id String @db.Uuid
role_id String role_id String @db.Uuid
resource resources @relation(fields: [resource_id], references: [id]) resource resources @relation(fields: [resource_id], references: [id])
role roles @relation(fields: [role_id], references: [id]) role roles @relation(fields: [role_id], references: [id])
created_at DateTime @default(now()) @db.Timestamptz(6) created_at DateTime @default(now()) @db.Timestamptz(6)

View File

@ -0,0 +1,201 @@
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
console.log('Starting seeding...');
// Create roles
const adminRole = await prisma.roles.upsert({
where: { name: 'admin' },
update: {},
create: {
name: 'admin',
description: 'Administrator with full access to all features',
},
});
const viewerRole = await prisma.roles.upsert({
where: { name: 'viewer' },
update: {},
create: {
name: 'viewer',
description: 'Read-only access to data',
},
});
const staffRole = await prisma.roles.upsert({
where: { name: 'staff' },
update: {},
create: {
name: 'staff',
description: 'Staff with limited administrative access',
},
});
console.log('Roles created:', { adminRole, viewerRole, staffRole });
// Create resources based on Prisma schema models
const resources = [
{
name: 'cities',
description: 'City data management',
attributes: {
fields: ['id', 'name', 'code', 'geographic_id', 'created_at', 'updated_at']
}
},
{
name: 'contact_messages',
description: 'Contact message management',
attributes: {
fields: ['id', 'name', 'email', 'phone', 'message_type', 'message_type_label', 'message', 'status', 'created_at', 'updated_at']
}
},
{
name: 'crime_cases',
description: 'Crime case management',
attributes: {
fields: ['id', 'crime_id', 'crime_category_id', 'date', 'time', 'location', 'latitude', 'longitude', 'description', 'victim_count', 'status', 'created_at', 'updated_at']
}
},
{
name: 'crime_categories',
description: 'Crime category management',
attributes: {
fields: ['id', 'name', 'description', 'created_at', 'updated_at']
}
},
{
name: 'crimes',
description: 'Crime data management',
attributes: {
fields: ['id', 'district_id', 'city_id', 'year', 'number_of_crime', 'rate', 'heat_map', 'created_at', 'updated_at']
}
},
{
name: 'demographics',
description: 'Demographic data management',
attributes: {
fields: ['id', 'district_id', 'city_id', 'province_id', 'year', 'population', 'population_density', 'poverty_rate', 'created_at', 'updated_at']
}
},
{
name: 'districts',
description: 'District data management',
attributes: {
fields: ['id', 'city_id', 'name', 'code', 'created_at', 'updated_at']
}
},
{
name: 'geographics',
description: 'Geographic data management',
attributes: {
fields: ['id', 'district_id', 'latitude', 'longitude', 'land_area', 'polygon', 'created_at', 'updated_at']
}
},
{
name: 'profiles',
description: 'User profile management',
attributes: {
fields: ['id', 'user_id', 'avatar', 'username', 'first_name', 'last_name', 'bio', 'address', 'birth_date']
}
},
{
name: 'users',
description: 'User account management',
attributes: {
fields: ['id', 'roles_id', 'email', 'phone', 'encrypted_password', 'invited_at', 'confirmed_at', 'email_confirmed_at', 'recovery_sent_at', 'last_sign_in_at', 'app_metadata', 'user_metadata', 'created_at', 'updated_at', 'banned_until', 'is_anonymous']
}
},
{
name: 'roles',
description: 'Role management',
attributes: {
fields: ['id', 'name', 'description', 'created_at', 'updated_at']
}
},
{
name: 'resources',
description: 'Resource management',
attributes: {
fields: ['id', 'name', 'description', 'instance_role', 'relations', 'attributes', 'created_at', 'updated_at']
}
},
{
name: 'permissions',
description: 'Permission management',
attributes: {
fields: ['id', 'action', 'resource_id', 'role_id', 'created_at', 'updated_at']
}
}
];
// Create resources in the database
for (const resource of resources) {
const createdResource = await prisma.resources.upsert({
where: { name: resource.name },
update: {},
create: {
name: resource.name,
description: resource.description,
attributes: resource.attributes
},
});
console.log(`Resource ${resource.name} created/updated with ID: ${createdResource.id}`);
}
// Set up basic permissions for each role
const allResources = await prisma.resources.findMany();
// Admin permissions - full access to all resources
for (const resource of allResources) {
await createPermissions(adminRole.id, resource.id, ['create', 'read', 'update', 'delete']);
}
// Viewer permissions - read-only access to all resources
for (const resource of allResources) {
await createPermissions(viewerRole.id, resource.id, ['read']);
}
// Staff permissions - mixed permissions based on resource
for (const resource of allResources) {
if (['roles', 'permissions', 'resources', 'users'].includes(resource.name)) {
// Staff can only read roles, permissions, resources and users
await createPermissions(staffRole.id, resource.id, ['read']);
} else {
// Staff can create, read, update but not delete other resources
await createPermissions(staffRole.id, resource.id, ['create', 'read', 'update']);
}
}
console.log('Seeding completed!');
}
async function createPermissions(roleId: string, resourceId: string, actions: string[]) {
for (const action of actions) {
await prisma.permissions.createMany({
data: {
action: action,
resource_id: resourceId,
role_id: roleId,
},
skipDuplicates: true // Skip if the permission already exists
}).catch((error) => {
console.error(`Error creating permission for role ${roleId} on resource ${resourceId}:`, error);
});
}
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});

View File

@ -0,0 +1,15 @@
import { ICreatePermissionSchema } from "@/src/entities/models/permissions/create-permission.model";
import { IPermissionsSchema } from "@/src/entities/models/permissions/permissions.model";
import { IUpdatePermissionSchema } from "@/src/entities/models/permissions/update-permission.model";
import { permissions } from "@prisma/client";
export interface IPermissionsRepository {
create(data: ICreatePermissionSchema): Promise<IPermissionsSchema>;
getById(id: string): Promise<IPermissionsSchema | null>;
getByRoleAndResource(roleId: string, resourceId: string): Promise<IPermissionsSchema[]>;
getByRole(role: string): Promise<IPermissionsSchema[]>;
getAll(): Promise<IPermissionsSchema[]>;
update(id: string, data: IUpdatePermissionSchema): Promise<IPermissionsSchema>;
delete(id: string): Promise<IPermissionsSchema>;
checkPermission(role: string, action: string, resource: string): Promise<boolean>;
}

View File

@ -0,0 +1,14 @@
import { ICreateResourceSchema } from "@/src/entities/models/resources/create-resources.model";
import { IResourcesSchema } from "@/src/entities/models/resources/resources.model";
import { IUpdateResourceSchema } from "@/src/entities/models/resources/update-resources.model";
import { resources } from "@prisma/client";
export interface IResourcesRepository {
create(data: ICreateResourceSchema): Promise<IResourcesSchema>;
getById(id: string): Promise<IResourcesSchema | null>;
getByName(name: string): Promise<IResourcesSchema | null>;
getByType(type: string): Promise<IResourcesSchema[] | null>;
getAll(): Promise<IResourcesSchema[]>;
update(id: string, data: IUpdateResourceSchema): Promise<IResourcesSchema>;
delete(id: string): Promise<IResourcesSchema>;
}

View File

@ -0,0 +1,13 @@
import { ICreateRoleSchema } from "@/src/entities/models/roles/create-roles.model";
import { IRolesSchema } from "@/src/entities/models/roles/roles.model";
import { IUpdateRoleSchema } from "@/src/entities/models/roles/update-roles.model";
import { roles } from "@prisma/client";
export interface IRolesRepository {
create(data: ICreateRoleSchema): Promise<IRolesSchema>;
getById(id: string): Promise<IRolesSchema | null>;
getByName(name: string): Promise<IRolesSchema | null>;
getAll(): Promise<IRolesSchema[]>;
update(id: string, data: IUpdateRoleSchema): Promise<IRolesSchema>;
delete(id: string): Promise<IRolesSchema>;
}

View File

@ -1,6 +1,6 @@
import { createAdminClient } from "@/app/_utils/supabase/admin"; import { createAdminClient } from "@/app/_utils/supabase/admin";
import { createClient } from "@/app/_utils/supabase/client"; import { createClient } from "@/app/_utils/supabase/client";
import { IUserSchema, UserResponse } from "@/src/entities/models/users/users.model"; import { IUserSchema, IUserSupabaseSchema, UserResponse } from "@/src/entities/models/users/users.model";
import { ITransaction } from "@/src/entities/models/transaction.interface"; import { ITransaction } from "@/src/entities/models/transaction.interface";
import { ICreateUserSchema } from "@/src/entities/models/users/create-user.model"; import { ICreateUserSchema } from "@/src/entities/models/users/create-user.model";
import { ICredentialUpdateUserSchema, IUpdateUserSchema } from "@/src/entities/models/users/update-user.model"; import { ICredentialUpdateUserSchema, IUpdateUserSchema } from "@/src/entities/models/users/update-user.model";
@ -16,8 +16,8 @@ export interface IUsersRepository {
getUserById(credential: ICredentialGetUserByIdSchema): Promise<IUserSchema | undefined>; getUserById(credential: ICredentialGetUserByIdSchema): Promise<IUserSchema | undefined>;
getUserByUsername(credential: ICredentialGetUserByUsernameSchema): Promise<IUserSchema | undefined>; getUserByUsername(credential: ICredentialGetUserByUsernameSchema): Promise<IUserSchema | undefined>;
getUserByEmail(credential: ICredentialGetUserByEmailSchema): Promise<IUserSchema | undefined>; getUserByEmail(credential: ICredentialGetUserByEmailSchema): Promise<IUserSchema | undefined>;
createUser(input: ICreateUserSchema, tx?: ITransaction): Promise<IUserSchema>; createUser(input: ICreateUserSchema, tx?: ITransaction): Promise<IUserSupabaseSchema>;
inviteUser(credential: ICredentialsInviteUserSchema, tx?: ITransaction): Promise<IUserSchema>; inviteUser(credential: ICredentialsInviteUserSchema, tx?: ITransaction): Promise<IUserSupabaseSchema>;
updateUser(credential: ICredentialUpdateUserSchema, input: Partial<IUpdateUserSchema>, tx?: ITransaction): Promise<IUserSchema>; updateUser(credential: ICredentialUpdateUserSchema, input: Partial<IUpdateUserSchema>, tx?: ITransaction): Promise<IUserSchema>;
deleteUser(credential: ICredentialsDeleteUserSchema, tx?: ITransaction): Promise<IUserSchema>; deleteUser(credential: ICredentialsDeleteUserSchema, tx?: ITransaction): Promise<IUserSchema>;
banUser(credential: ICredentialsBanUserSchema, input: IBanUserSchema, tx?: ITransaction): Promise<IUserSchema>; banUser(credential: ICredentialsBanUserSchema, input: IBanUserSchema, tx?: ITransaction): Promise<IUserSchema>;

View File

@ -17,4 +17,5 @@ export interface IAuthenticationService {
sendMagicLink(credentials: ISendMagicLinkSchema): Promise<void> sendMagicLink(credentials: ISendMagicLinkSchema): Promise<void>
sendPasswordRecovery(credentials: ISendPasswordRecoverySchema): Promise<void> sendPasswordRecovery(credentials: ISendPasswordRecoverySchema): Promise<void>
verifyOtp(credentials: IVerifyOtpSchema): Promise<void> verifyOtp(credentials: IVerifyOtpSchema): Promise<void>
checkPermission(userId: string, action: string, resource: string): Promise<boolean>;
} }

View File

@ -0,0 +1,24 @@
import { IInstrumentationService } from "../../services/instrumentation.service.interface";
import { NotFoundError } from "@/src/entities/errors/common";
import { IAuthenticationService } from "../../services/authentication.service.interface";
import { IUsersRepository } from "../../repositories/users.repository.interface";
export type ICheckPermissionsUseCase = ReturnType<typeof checkPermissionsUseCase>;
export const checkPermissionsUseCase = (
instrumentationService: IInstrumentationService,
authenticationService: IAuthenticationService,
) => async (userId: string, action: string, resource: string): Promise<boolean> => {
return await instrumentationService.startSpan({ name: "Check Permission Use Case", op: "function" },
async () => {
const permission = await authenticationService.checkPermission(userId, action, resource);
if (!permission) {
throw new NotFoundError("Permission not found");
}
return permission;
}
);
};

View File

@ -0,0 +1,28 @@
import { ICreatePermissionSchema } from "@/src/entities/models/permissions/create-permission.model"
import { IInstrumentationService } from "../../services/instrumentation.service.interface"
import { IPermissionsRepository } from "../../repositories/permissions.repository.interface"
import { permissions } from "@prisma/client"
import { AlreadyExistsError } from "@/src/entities/errors/common"
import { IPermissionsSchema } from "@/src/entities/models/permissions/permissions.model"
export type ICreatePermissionUseCase = ReturnType<typeof createPermissionUseCase>
export const createPermissionUseCase = (
instrumentationService: IInstrumentationService,
permissionsRepository: IPermissionsRepository,
) => async (input: ICreatePermissionSchema): Promise<IPermissionsSchema> => {
return await instrumentationService.startSpan({ name: "Create Permission Use Case", op: "function" },
async () => {
const existingPermission = await permissionsRepository.getByRoleAndResource(input.role_id, input.resource_id)
if (existingPermission) {
throw new AlreadyExistsError("Permission already exists")
}
const permission = await permissionsRepository.create(input)
return permission
}
)
}

View File

@ -0,0 +1,27 @@
import { IPermissionsSchema } from "@/src/entities/models/permissions/permissions.model";
import { IPermissionsRepository } from "../../repositories/permissions.repository.interface";
import { IInstrumentationService } from "../../services/instrumentation.service.interface";
import { NotFoundError } from "@/src/entities/errors/common";
import { IDeletePermissionsSchema } from "@/src/entities/models/permissions/delete-permissions.model";
export type IDeletePermissionUseCase = ReturnType<typeof deletePermissionUseCase>;
export const deletePermissionUseCase = (
instrumentationService: IInstrumentationService,
permissionsRepository: IPermissionsRepository,
) => async (input: IDeletePermissionsSchema): Promise<IPermissionsSchema> => {
return await instrumentationService.startSpan({ name: "Delete Permissions Use Case", op: "function" },
async () => {
const permissions = await permissionsRepository.getById(input.id);
if (!permissions) {
throw new NotFoundError("Permissions not found");
}
const deletedPermissions = await permissionsRepository.delete(input.id);
return deletedPermissions;
}
);
}

View File

@ -0,0 +1,20 @@
import { IPermissionsSchema } from "@/src/entities/models/permissions/permissions.model";
import { IPermissionsRepository } from "../../repositories/permissions.repository.interface";
import { IInstrumentationService } from "../../services/instrumentation.service.interface";
export type IGetAllPermissionsUseCase = ReturnType<typeof getAllPermissionsUseCase>;
export const getAllPermissionsUseCase = (
instrumentationService: IInstrumentationService,
permissionsRepository: IPermissionsRepository,
) => async (): Promise<IPermissionsSchema[]> => {
return await instrumentationService.startSpan({ name: "Get All Permissions Use Case", op: "function" },
async () => {
const permissions = await permissionsRepository.getAll()
return permissions
}
)
}

View File

@ -0,0 +1,24 @@
import { IPermissionsSchema } from "@/src/entities/models/permissions/permissions.model";
import { IPermissionsRepository } from "../../repositories/permissions.repository.interface";
import { IInstrumentationService } from "../../services/instrumentation.service.interface";
import { NotFoundError } from "@/src/entities/errors/common";
export type IGetPermissionByIdUseCase = ReturnType<typeof getPermissionByIdUseCase>;
export const getPermissionByIdUseCase = (
instrumentationService: IInstrumentationService,
permissionsRepository: IPermissionsRepository,
) => async (id: string): Promise<IPermissionsSchema> => {
return await instrumentationService.startSpan({ name: "Get Permissions By Id Use Case", op: "function" },
async () => {
const permissions = await permissionsRepository.getById(id)
if (!permissions) {
throw new NotFoundError("Permissions not found")
}
return permissions
}
)
}

View File

@ -0,0 +1,24 @@
import { IPermissionsSchema } from "@/src/entities/models/permissions/permissions.model";
import { IPermissionsRepository } from "../../repositories/permissions.repository.interface";
import { IInstrumentationService } from "../../services/instrumentation.service.interface";
import { NotFoundError } from "@/src/entities/errors/common";
export type IGetPermissionByRoleAndResourcesUseCase = ReturnType<typeof getPermissionByRoleAndResourcesUseCase>;
export const getPermissionByRoleAndResourcesUseCase = (
instrumentationService: IInstrumentationService,
permissionsRepository: IPermissionsRepository,
) => async (roleId: string, resourceId: string): Promise<IPermissionsSchema[]> => {
return await instrumentationService.startSpan({ name: "Get Permissions By Role And Resources Use Case", op: "function" },
async () => {
const permissions = await permissionsRepository.getByRoleAndResource(roleId, resourceId)
if (!permissions) {
throw new NotFoundError("Permissions not found")
}
return permissions
}
)
}

View File

@ -0,0 +1,24 @@
import { NotFoundError } from "@/src/entities/errors/common";
import { IPermissionsSchema } from "@/src/entities/models/permissions/permissions.model";
import { IInstrumentationService } from "../../services/instrumentation.service.interface";
import { IPermissionsRepository } from "../../repositories/permissions.repository.interface";
export type IGetPermissionByRoleUseCase = ReturnType<typeof getPermissionByRoleUseCase>;
export const getPermissionByRoleUseCase = (
instrumentationService: IInstrumentationService,
permissionsRepository: IPermissionsRepository,
) => async (roleId: string): Promise<IPermissionsSchema[]> => {
return await instrumentationService.startSpan({ name: "Get Permissions By Role Use Case", op: "function" },
async () => {
const permissions = await permissionsRepository.getByRole(roleId)
if (!permissions) {
throw new NotFoundError("Permissions not found")
}
return permissions
}
)
}

View File

@ -0,0 +1,28 @@
import { IUpdatePermissionSchema } from "@/src/entities/models/permissions/update-permission.model";
import { IPermissionsRepository } from "../../repositories/permissions.repository.interface";
import { IInstrumentationService } from "../../services/instrumentation.service.interface";
import { IPermissionsSchema } from "@/src/entities/models/permissions/permissions.model";
import { NotFoundError } from "@/src/entities/errors/common";
export type IUpdatePermissionUseCase = ReturnType<typeof updatePermissionUseCase>;
export const updatePermissionUseCase = (
instrumentationService: IInstrumentationService,
permissionsRepository: IPermissionsRepository,
) => async (id: string, input: IUpdatePermissionSchema): Promise<IPermissionsSchema> => {
return await instrumentationService.startSpan({ name: "Update Permission Use Case", op: "function" },
async () => {
const permission = await permissionsRepository.getById(id)
if (!permission) {
throw new NotFoundError("Permission not found")
}
const updatedPermission = await permissionsRepository.update(id, input)
return updatedPermission
}
)
}

View File

@ -0,0 +1,27 @@
import { ICreateResourceSchema } from "@/src/entities/models/resources/create-resources.model";
import { IInstrumentationService } from "../../services/instrumentation.service.interface";
import { IResourcesRepository } from "../../repositories/resources.repository.interface";
import { IResourcesSchema } from "@/src/entities/models/resources/resources.model";
import { AlreadyExistsError } from "@/src/entities/errors/common";
export type ICreateResourceUseCase = ReturnType<typeof createResourceUseCase>;
export const createResourceUseCase = (
instrumentationService: IInstrumentationService,
resourcesRepository: IResourcesRepository,
) => async (input: ICreateResourceSchema): Promise<IResourcesSchema> => {
return await instrumentationService.startSpan({ name: "Create Resource Use Case", op: "function" },
async () => {
const existingResource = await resourcesRepository.getByName(input.name);
if (existingResource) {
throw new AlreadyExistsError("Resource already exists");
}
const resource = await resourcesRepository.create(input);
return resource;
}
);
};

View File

@ -0,0 +1,26 @@
import { IInstrumentationService } from "../../services/instrumentation.service.interface";
import { IResourcesRepository } from "../../repositories/resources.repository.interface";
import { IResourcesSchema } from "@/src/entities/models/resources/resources.model";
import { NotFoundError } from "@/src/entities/errors/common";
export type IDeleteResourceUseCase = ReturnType<typeof deleteResourceUseCase>;
export const deleteResourceUseCase = (
instrumentationService: IInstrumentationService,
resourcesRepository: IResourcesRepository,
) => async (id: string): Promise<IResourcesSchema> => {
return await instrumentationService.startSpan({ name: "Delete Resource Use Case", op: "function" },
async () => {
const existingResource = await resourcesRepository.getById(id);
if (!existingResource) {
throw new NotFoundError("Resource not found");
}
const deletedResource = await resourcesRepository.delete(id);
return deletedResource;
}
);
};

View File

@ -0,0 +1,24 @@
import { IInstrumentationService } from "../../services/instrumentation.service.interface";
import { IResourcesRepository } from "../../repositories/resources.repository.interface";
import { IResourcesSchema } from "@/src/entities/models/resources/resources.model";
import { NotFoundError } from "@/src/entities/errors/common";
export type IGetResourceByIdUseCase = ReturnType<typeof getResourceByIdUseCase>;
export const getResourceByIdUseCase = (
instrumentationService: IInstrumentationService,
resourcesRepository: IResourcesRepository,
) => async (id: string): Promise<IResourcesSchema> => {
return await instrumentationService.startSpan({ name: "Get Resource By ID Use Case", op: "function" },
async () => {
const resource = await resourcesRepository.getById(id);
if (!resource) {
throw new NotFoundError("Resource not found");
}
return resource;
}
);
};

View File

@ -0,0 +1,24 @@
import { IInstrumentationService } from "../../services/instrumentation.service.interface";
import { IResourcesRepository } from "../../repositories/resources.repository.interface";
import { IResourcesSchema } from "@/src/entities/models/resources/resources.model";
import { NotFoundError } from "@/src/entities/errors/common";
export type IGetResourceByNameUseCase = ReturnType<typeof getResourceByNameUseCase>;
export const getResourceByNameUseCase = (
instrumentationService: IInstrumentationService,
resourcesRepository: IResourcesRepository,
) => async (name: string): Promise<IResourcesSchema> => {
return await instrumentationService.startSpan({ name: "Get Resource By Name Use Case", op: "function" },
async () => {
const resource = await resourcesRepository.getByName(name);
if (!resource) {
throw new NotFoundError("Resource not found");
}
return resource;
}
);
};

View File

@ -0,0 +1,19 @@
import { IInstrumentationService } from "../../services/instrumentation.service.interface";
import { IResourcesRepository } from "../../repositories/resources.repository.interface";
import { IResourcesSchema } from "@/src/entities/models/resources/resources.model";
export type IGetResourcesByTypeUseCase = ReturnType<typeof getResourcesByTypeUseCase>;
export const getResourcesByTypeUseCase = (
instrumentationService: IInstrumentationService,
resourcesRepository: IResourcesRepository,
) => async (type: string): Promise<IResourcesSchema[]> => {
return await instrumentationService.startSpan({ name: "Get Resources By Type Use Case", op: "function" },
async () => {
const resources = await resourcesRepository.getByType(type);
return resources || [];
}
);
};

View File

@ -0,0 +1,27 @@
import { IUpdateResourceSchema } from "@/src/entities/models/resources/update-resources.model";
import { IInstrumentationService } from "../../services/instrumentation.service.interface";
import { IResourcesRepository } from "../../repositories/resources.repository.interface";
import { IResourcesSchema } from "@/src/entities/models/resources/resources.model";
import { NotFoundError } from "@/src/entities/errors/common";
export type IUpdateResourceUseCase = ReturnType<typeof updateResourceUseCase>;
export const updateResourceUseCase = (
instrumentationService: IInstrumentationService,
resourcesRepository: IResourcesRepository,
) => async (id: string, data: IUpdateResourceSchema): Promise<IResourcesSchema> => {
return await instrumentationService.startSpan({ name: "Update Resource Use Case", op: "function" },
async () => {
const existingResource = await resourcesRepository.getById(id);
if (!existingResource) {
throw new NotFoundError("Resource not found");
}
const updatedResource = await resourcesRepository.update(id, data);
return updatedResource;
}
);
};

View File

@ -0,0 +1,28 @@
import { IInstrumentationService } from "../../services/instrumentation.service.interface";
import { IRolesRepository } from "../../repositories/roles.repository.interface";
import { IRolesSchema } from "@/src/entities/models/roles/roles.model";
import { AlreadyExistsError } from "@/src/entities/errors/common";
import { ICreateRoleSchema } from "@/src/entities/models/roles/create-roles.model";
export type ICreateRoleUseCase = ReturnType<typeof createRoleUseCase>;
export const createRoleUseCase = (
instrumentationService: IInstrumentationService,
rolesRepository: IRolesRepository,
) => async (input: ICreateRoleSchema): Promise<IRolesSchema> => {
return await instrumentationService.startSpan({ name: "Create Role Use Case", op: "function" },
async () => {
const existingRole = await rolesRepository.getByName(input.name);
if (existingRole) {
throw new AlreadyExistsError("Role already exists");
}
const role = await rolesRepository.create(input);
return role;
}
);
};

View File

@ -0,0 +1,26 @@
import { IInstrumentationService } from "../../services/instrumentation.service.interface";
import { IRolesRepository } from "../../repositories/roles.repository.interface";
import { IRolesSchema } from "@/src/entities/models/roles/roles.model";
import { NotFoundError } from "@/src/entities/errors/common";
export type IDeleteRoleUseCase = ReturnType<typeof deleteRoleUseCase>;
export const deleteRoleUseCase = (
instrumentationService: IInstrumentationService,
rolesRepository: IRolesRepository,
) => async (id: string): Promise<IRolesSchema> => {
return await instrumentationService.startSpan({ name: "Delete Role Use Case", op: "function" },
async () => {
const existingRole = await rolesRepository.getById(id);
if (!existingRole) {
throw new NotFoundError("Role not found");
}
const deletedRole = await rolesRepository.delete(id);
return deletedRole;
}
);
};

View File

@ -0,0 +1,24 @@
import { IInstrumentationService } from "../../services/instrumentation.service.interface";
import { IRolesRepository } from "../../repositories/roles.repository.interface";
import { IRolesSchema } from "@/src/entities/models/roles/roles.model";
import { NotFoundError } from "@/src/entities/errors/common";
export type IGetRoleByIdUseCase = ReturnType<typeof getRoleByIdUseCase>;
export const getRoleByIdUseCase = (
instrumentationService: IInstrumentationService,
rolesRepository: IRolesRepository,
) => async (id: string): Promise<IRolesSchema> => {
return await instrumentationService.startSpan({ name: "Get Role By ID Use Case", op: "function" },
async () => {
const role = await rolesRepository.getById(id);
if (!role) {
throw new NotFoundError("Role not found");
}
return role;
}
);
};

View File

@ -0,0 +1,24 @@
import { IInstrumentationService } from "../../services/instrumentation.service.interface";
import { IRolesRepository } from "../../repositories/roles.repository.interface";
import { IRolesSchema } from "@/src/entities/models/roles/roles.model";
import { NotFoundError } from "@/src/entities/errors/common";
export type IGetRoleByNameUseCase = ReturnType<typeof getRoleByNameUseCase>;
export const getRoleByNameUseCase = (
instrumentationService: IInstrumentationService,
rolesRepository: IRolesRepository,
) => async (name: string): Promise<IRolesSchema> => {
return await instrumentationService.startSpan({ name: "Get Role By Name Use Case", op: "function" },
async () => {
const role = await rolesRepository.getByName(name);
if (!role) {
throw new NotFoundError("Role not found");
}
return role;
}
);
};

View File

@ -0,0 +1,27 @@
import { IInstrumentationService } from "../../services/instrumentation.service.interface";
import { IRolesRepository } from "../../repositories/roles.repository.interface";
import { IRolesSchema } from "@/src/entities/models/roles/roles.model";
import { NotFoundError } from "@/src/entities/errors/common";
import { IUpdateRoleSchema } from "@/src/entities/models/roles/update-roles.model";
export type IUpdateRoleUseCase = ReturnType<typeof updateRoleUseCase>;
export const updateRoleUseCase = (
instrumentationService: IInstrumentationService,
rolesRepository: IRolesRepository,
) => async (id: string, data: IUpdateRoleSchema): Promise<IRolesSchema> => {
return await instrumentationService.startSpan({ name: "Update Role Use Case", op: "function" },
async () => {
const existingRole = await rolesRepository.getById(id);
if (!existingRole) {
throw new NotFoundError("Role not found");
}
const updatedRole = await rolesRepository.update(id, data);
return updatedRole;
}
);
};

View File

@ -2,7 +2,7 @@ import { AuthenticationError } from "@/src/entities/errors/auth"
import { IUsersRepository } from "../../repositories/users.repository.interface" import { IUsersRepository } from "../../repositories/users.repository.interface"
import { IAuthenticationService } from "../../services/authentication.service.interface" import { IAuthenticationService } from "../../services/authentication.service.interface"
import { IInstrumentationService } from "../../services/instrumentation.service.interface" import { IInstrumentationService } from "../../services/instrumentation.service.interface"
import { IUserSchema } from "@/src/entities/models/users/users.model" import { IUserSchema, IUserSupabaseSchema } from "@/src/entities/models/users/users.model"
import { InputParseError } from "@/src/entities/errors/common" import { InputParseError } from "@/src/entities/errors/common"
import { ICreateUserSchema } from "@/src/entities/models/users/create-user.model" import { ICreateUserSchema } from "@/src/entities/models/users/create-user.model"
@ -12,7 +12,7 @@ export type ICreateUserUseCase = ReturnType<typeof createUserUseCase>
export const createUserUseCase = ( export const createUserUseCase = (
instrumentationService: IInstrumentationService, instrumentationService: IInstrumentationService,
usersRepository: IUsersRepository, usersRepository: IUsersRepository,
) => async (input: ICreateUserSchema): Promise<IUserSchema> => { ) => async (input: ICreateUserSchema): Promise<IUserSupabaseSchema> => {
return await instrumentationService.startSpan({ name: "createUser Use Case", op: "function" }, return await instrumentationService.startSpan({ name: "createUser Use Case", op: "function" },
async () => { async () => {

View File

@ -2,7 +2,7 @@ import { AuthenticationError } from "@/src/entities/errors/auth"
import { IUsersRepository } from "../../repositories/users.repository.interface" import { IUsersRepository } from "../../repositories/users.repository.interface"
import { IAuthenticationService } from "../../services/authentication.service.interface" import { IAuthenticationService } from "../../services/authentication.service.interface"
import { IInstrumentationService } from "../../services/instrumentation.service.interface" import { IInstrumentationService } from "../../services/instrumentation.service.interface"
import { IUserSchema } from "@/src/entities/models/users/users.model" import { IUserSchema, IUserSupabaseSchema } from "@/src/entities/models/users/users.model"
import { ICredentialsInviteUserSchema } from "@/src/entities/models/users/invite-user.model" import { ICredentialsInviteUserSchema } from "@/src/entities/models/users/invite-user.model"
@ -11,7 +11,7 @@ export type IInviteUserUseCase = ReturnType<typeof inviteUserUseCase>
export const inviteUserUseCase = ( export const inviteUserUseCase = (
instrumentationService: IInstrumentationService, instrumentationService: IInstrumentationService,
usersRepository: IUsersRepository, usersRepository: IUsersRepository,
) => async (credential: ICredentialsInviteUserSchema): Promise<IUserSchema> => { ) => async (credential: ICredentialsInviteUserSchema): Promise<IUserSupabaseSchema> => {
return await instrumentationService.startSpan({ name: "inviteUser Use Case", op: "function" }, return await instrumentationService.startSpan({ name: "inviteUser Use Case", op: "function" },
async () => { async () => {

View File

@ -18,6 +18,12 @@ export class InputParseError extends Error {
} }
} }
export class AlreadyExistsError extends Error {
constructor(message: string, options?: ErrorOptions) {
super(message, options);
}
}
export class ServerActionError extends Error { export class ServerActionError extends Error {
code: string; code: string;

View File

@ -5,7 +5,7 @@ export const SessionSchema = z.object({
user: UserSchema.pick({ user: UserSchema.pick({
id: true, id: true,
email: true, email: true,
role: true, roles_id: true,
}), }),
expiresAt: z.number().optional(), expiresAt: z.number().optional(),
}); });

View File

@ -0,0 +1,21 @@
import { z } from "zod";
export interface CreatePermissionDto {
action: string;
resource_id: string;
role_id: string;
}
export const CreatePermissionSchema = z.object({
action: z.string().min(1, { message: "Action is required" }),
resource_id: z.string().min(1, { message: "Resource ID is required" }),
role_id: z.string().min(1, { message: "Role ID is required" }),
})
export type ICreatePermissionSchema = z.infer<typeof CreatePermissionSchema>;
export const defaultICreatePermissionSchemaValues: ICreatePermissionSchema = {
action: "",
resource_id: "",
role_id: "",
}

View File

@ -0,0 +1,12 @@
import { z } from "zod";
export const DeletePermissionsSchema = z.object({
id: z.string().uuid(),
})
export type IDeletePermissionsSchema = z.infer<typeof DeletePermissionsSchema>
export const defaultIDeletePermissionsSchemaValues: IDeletePermissionsSchema = {
id: "",
}

View File

@ -0,0 +1,22 @@
import { z } from "zod";
type permissions = {
id: string;
action: string;
role_id: string;
resource_id: string;
created_at: Date;
updated_at: Date;
}
export const PermissionsSchema = z.object({
id: z.string().uuid(),
action: z.string().min(1).max(255),
role_id: z.string().uuid(),
resource_id: z.string().uuid(),
created_at: z.date(),
updated_at: z.date()
})
export type IPermissionsSchema = z.infer<typeof PermissionsSchema>;

View File

@ -0,0 +1,15 @@
import { z } from "zod";
export const UpdatePermissionSchema = z.object({
action: z.string().min(1, { message: "Action is required" }),
resource_id: z.string().min(1, { message: "Resource ID is required" }),
role_id: z.string().min(1, { message: "Role ID is required" }),
})
export type IUpdatePermissionSchema = z.infer<typeof UpdatePermissionSchema>;
export const defaultIUpdatePermissionSchemaValues: IUpdatePermissionSchema = {
action: "",
resource_id: "",
role_id: "",
}

View File

@ -0,0 +1,30 @@
import { z } from "zod";
export interface CreateResourceDto {
name: string;
type?: string;
description?: string;
instance_role?: string;
relations?: string;
attributes?: Record<string, any>;
}
export const CreateResourceSchema = z.object({
name: z.string().min(1, { message: "Name is required" }),
type: z.string().optional(),
description: z.string().optional(),
instance_role: z.string().optional(),
relations: z.string().optional(),
attributes: z.record(z.any()).optional(),
})
export type ICreateResourceSchema = z.infer<typeof CreateResourceSchema>;
export const defaultICreateResourceSchemaValues: ICreateResourceSchema = {
name: "",
type: undefined,
description: undefined,
instance_role: undefined,
relations: undefined,
attributes: {},
}

View File

@ -0,0 +1,28 @@
import { JsonValue } from "@prisma/client/runtime/library";
import { z } from "zod";
type resources = {
id: string;
name: string;
type: string | null;
description: string | null;
instance_role: string | null;
relations: string | null;
attributes: JsonValue | null;
created_at: Date;
updated_at: Date;
}
export const ResourcesSchema = z.object({
id: z.string().uuid(),
name: z.string().min(1).max(255),
type: z.string().nullable(),
description: z.string().nullable(),
instance_role: z.string().nullable(),
relations: z.string().nullable(),
attributes: z.any().nullable(),
created_at: z.date(),
updated_at: z.date(),
})
export type IResourcesSchema = z.infer<typeof ResourcesSchema>;

View File

@ -0,0 +1,30 @@
import { z } from "zod";
export interface UpdateResourceDto {
name?: string;
type?: string;
description?: string;
instance_role?: string;
relations?: string;
attributes?: Record<string, any>;
}
export const UpdateResourceSchema = z.object({
name: z.string().optional(),
type: z.string().optional(),
description: z.string().optional(),
instance_role: z.string().optional(),
relations: z.string().optional(),
attributes: z.record(z.any()).optional(),
})
export type IUpdateResourceSchema = z.infer<typeof UpdateResourceSchema>;
export const defaultIUpdateResourceSchemaValues: IUpdateResourceSchema = {
name: undefined,
type: undefined,
description: undefined,
instance_role: undefined,
relations: undefined,
attributes: {},
}

View File

@ -0,0 +1,18 @@
import { z } from "zod";
export interface CreateRoleDto {
name: string;
description?: string;
}
export const CreateRoleSchema = z.object({
name: z.string().min(1, { message: "Name is required" }),
description: z.string().optional(),
})
export type ICreateRoleSchema = z.infer<typeof CreateRoleSchema>;
export const defaultICreateRoleSchemaValues: ICreateRoleSchema = {
name: "",
description: undefined,
}

View File

@ -0,0 +1,11 @@
import { z } from "zod";
export const RoleSchema = z.object({
id: z.string(),
name: z.string(),
description: z.string().nullable().optional(),
created_at: z.union([z.string(), z.date()]).nullable().optional(),
updated_at: z.union([z.string(), z.date()]).nullable().optional(),
})
export type IRolesSchema = z.infer<typeof RoleSchema>;

View File

@ -0,0 +1,18 @@
import { z } from "zod";
export interface UpdateRoleDto {
name?: string;
description?: string;
}
export const UpdateRoleSchema = z.object({
name: z.string().optional(),
description: z.string().optional(),
})
export type IUpdateRoleSchema = z.infer<typeof UpdateRoleSchema>;
export const defaultIUpdateRoleSchemaValues: IUpdateRoleSchema = {
name: undefined,
description: undefined,
}

View File

@ -4,7 +4,7 @@ export const UpdateUserSchema = z.object({
email: z.string().email().optional(), email: z.string().email().optional(),
email_confirmed_at: z.boolean().optional(), email_confirmed_at: z.boolean().optional(),
encrypted_password: z.string().optional(), encrypted_password: z.string().optional(),
role: z.enum(["user", "staff", "admin"]).optional(), roles_id: z.string().optional(), // Sesuai dengan model di Prisma
phone: z.string().optional(), phone: z.string().optional(),
phone_confirmed_at: z.boolean().optional(), phone_confirmed_at: z.boolean().optional(),
invited_at: z.union([z.string(), z.date()]).optional(), invited_at: z.union([z.string(), z.date()]).optional(),
@ -34,7 +34,7 @@ export const defaultValueUpdateUserSchema: IUpdateUserSchema = {
email: undefined, email: undefined,
email_confirmed_at: undefined, email_confirmed_at: undefined,
encrypted_password: undefined, encrypted_password: undefined,
role: "user", roles_id: undefined,
phone: undefined, phone: undefined,
phone_confirmed_at: undefined, phone_confirmed_at: undefined,
invited_at: undefined, invited_at: undefined,

View File

@ -1,6 +1,7 @@
import { z } from "zod"; import { z } from "zod";
import { AuthError } from "@supabase/supabase-js"; import { AuthError } from "@supabase/supabase-js";
import { RoleSchema } from "../roles/roles.model";
const timestampSchema = z.union([z.string(), z.date()]).nullable(); const timestampSchema = z.union([z.string(), z.date()]).nullable();
@ -37,7 +38,7 @@ const timestampSchema = z.union([z.string(), z.date()]).nullable();
export const UserSchema = z.object({ export const UserSchema = z.object({
id: z.string(), id: z.string(),
role: z.string().optional(), roles_id: z.string().optional(), // Sesuaikan dengan field di Prisma
email: z.string().email().optional(), email: z.string().email().optional(),
email_confirmed_at: z.union([z.string(), z.date()]).nullable().optional(), email_confirmed_at: z.union([z.string(), z.date()]).nullable().optional(),
encrypted_password: z.string().nullable().optional(), encrypted_password: z.string().nullable().optional(),
@ -64,10 +65,39 @@ export const UserSchema = z.object({
}) })
.nullable() .nullable()
.optional(), .optional(),
role: RoleSchema.optional(),
}); });
export type IUserSchema = z.infer<typeof UserSchema>; export type IUserSchema = z.infer<typeof UserSchema>;
export const UserSupabaseSchema = z.object({
id: z.string(),
app_metadata: z.any().optional(),
user_metadata: z.any().optional(),
aud: z.string(),
confirmation_sent_at: z.union([z.string(), z.date()]).nullable().optional(),
recovery_sent_at: z.union([z.string(), z.date()]).nullable().optional(),
email_change_sent_at: z.union([z.string(), z.date()]).nullable().optional(),
new_email: z.string().nullable().optional(),
new_phone: z.string().nullable().optional(),
invited_at: z.union([z.string(), z.date()]).nullable().optional(),
action_link: z.string().nullable().optional(),
email: z.string().email().nullable().optional(),
phone: z.string().nullable().optional(),
created_at: z.union([z.string(), z.date()]),
confirmed_at: z.union([z.string(), z.date()]).nullable().optional(),
email_confirmed_at: z.union([z.string(), z.date()]).nullable().optional(),
phone_confirmed_at: z.union([z.string(), z.date()]).nullable().optional(),
last_sign_in_at: z.union([z.string(), z.date()]).nullable().optional(),
role: z.string().nullable().optional(),
updated_at: z.union([z.string(), z.date()]).nullable().optional(),
identities: z.array(z.any()).nullable().optional(),
is_anonymous: z.boolean().optional(),
factors: z.array(z.any()).nullable().optional(),
})
export type IUserSupabaseSchema = z.infer<typeof UserSupabaseSchema>;
export const ProfileSchema = z.object({ export const ProfileSchema = z.object({
id: z.string(), id: z.string(),
user_id: z.string(), user_id: z.string(),

View File

@ -0,0 +1,86 @@
import db from '@/prisma/db';
import { IPermissionsRepository } from '@/src/application/repositories/permissions.repository.interface';
import { ICrashReporterService } from '@/src/application/services/crash-reporter.service.interface';
import { IInstrumentationService } from '@/src/application/services/instrumentation.service.interface';
import { ICreatePermissionSchema } from '@/src/entities/models/permissions/create-permission.model';
import { IPermissionsSchema } from '@/src/entities/models/permissions/permissions.model';
import { IUpdatePermissionSchema } from '@/src/entities/models/permissions/update-permission.model';
import { PrismaClient, permissions } from '@prisma/client';
export class PermissionsRepository implements IPermissionsRepository {
constructor(
private readonly instrumentationService: IInstrumentationService,
private readonly crashReporterService: ICrashReporterService,
) { }
async create(data: ICreatePermissionSchema): Promise<IPermissionsSchema> {
return db.permissions.create({
data: {
action: data.action,
resource_id: data.resource_id,
role_id: data.role_id
}
});
}
async getById(id: string): Promise<permissions | null> {
return db.permissions.findUnique({
where: { id }
});
}
async getByRoleAndResource(roleId: string, resourceId: string): Promise<IPermissionsSchema[]> {
return db.permissions.findMany({
where: {
role_id: roleId,
resource_id: resourceId
}
});
}
async getByRole(roleId: string): Promise<IPermissionsSchema[]> {
return db.permissions.findMany({
where: {
role_id: roleId
},
include: {
resource: true
}
});
}
async getAll(): Promise<IPermissionsSchema[]> {
return db.permissions.findMany();
}
async update(id: string, data: IUpdatePermissionSchema): Promise<IPermissionsSchema> {
return db.permissions.update({
where: { id },
data: {
action: data.action
}
});
}
async delete(id: string): Promise<IPermissionsSchema> {
return db.permissions.delete({
where: { id }
});
}
async checkPermission(role: string, action: string, resource: string): Promise<boolean> {
const result = await db.permissions.findFirst({
where: {
role: {
name: role
},
action: action,
resource: {
name: resource
}
}
});
return !!result;
}
}

View File

@ -0,0 +1,134 @@
import db from "@/prisma/db";
import { IResourcesRepository } from "@/src/application/repositories/resources.repository.interface";
import { ICrashReporterService } from "@/src/application/services/crash-reporter.service.interface";
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
import { ICreateResourceSchema } from "@/src/entities/models/resources/create-resources.model";
import { IResourcesSchema } from "@/src/entities/models/resources/resources.model";
import { resources } from "@prisma/client";
export class ResourcesRepository implements IResourcesRepository {
constructor(
private readonly instrumentationService: IInstrumentationService,
private readonly crashReporterService: ICrashReporterService,
) { }
async getById(id: string): Promise<IResourcesSchema | null> {
return this.instrumentationService.startSpan(
{ name: "Find Resource By ID", op: "function", attributes: {} },
async () => {
try {
return await db.resources.findUnique({
where: { id }
});
} catch (error) {
this.crashReporterService.report(error);
throw error;
}
}
)
}
async getByName(name: string): Promise<IResourcesSchema | null> {
return this.instrumentationService.startSpan(
{ name: "Find Resource By Name", op: "function", attributes: {} },
async () => {
try {
return await db.resources.findUnique({
where: { name }
});
} catch (error) {
this.crashReporterService.report(error);
throw error;
}
}
)
}
async getByType(type: string): Promise<IResourcesSchema[] | null> {
return this.instrumentationService.startSpan(
{ name: "Find Resources By Type", op: "function", attributes: {} },
async () => {
try {
return await db.resources.findMany({
where: { type }
});
} catch (error) {
this.crashReporterService.report(error);
throw error;
}
}
)
}
async getAll(): Promise<IResourcesSchema[]> {
return this.instrumentationService.startSpan(
{ name: "Find All Resources", op: "function", attributes: {} },
async () => {
try {
return await db.resources.findMany();
} catch (error) {
this.crashReporterService.report(error);
throw error;
}
}
)
}
async create(data: ICreateResourceSchema): Promise<IResourcesSchema> {
return this.instrumentationService.startSpan(
{ name: "Create Resource", op: "function", attributes: {} },
async () => {
try {
return await db.resources.create({
data: {
name: data.name,
description: data.description,
type: data.type,
}
});
} catch (error) {
this.crashReporterService.report(error);
throw error;
}
}
)
}
async update(id: string, data: ICreateResourceSchema): Promise<IResourcesSchema> {
return this.instrumentationService.startSpan(
{ name: "Update Resource", op: "function", attributes: {} },
async () => {
try {
return await db.resources.update({
where: { id },
data: {
name: data.name,
description: data.description,
type: data.type,
}
});
} catch (error) {
this.crashReporterService.report(error);
throw error;
}
}
)
}
async delete(id: string): Promise<IResourcesSchema> {
return this.instrumentationService.startSpan(
{ name: "Delete Resource", op: "function", attributes: {} },
async () => {
try {
return await db.resources.delete({
where: { id }
});
} catch (error) {
this.crashReporterService.report(error);
throw error;
}
}
)
}
}

View File

@ -0,0 +1,115 @@
import db from "@/prisma/db";
import { IRolesRepository } from "@/src/application/repositories/roles.repository.interface";
import { ICrashReporterService } from "@/src/application/services/crash-reporter.service.interface";
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
import { ICreateRoleSchema } from "@/src/entities/models/roles/create-roles.model";
import { IRolesSchema } from "@/src/entities/models/roles/roles.model";
import { roles } from "@prisma/client";
export class RolesRepository implements IRolesRepository {
constructor(
private readonly instrumentationService: IInstrumentationService,
private readonly crashReporterService: ICrashReporterService,
) { }
async getById(id: string): Promise<IRolesSchema | null> {
return this.instrumentationService.startSpan(
{ name: "Find Role By ID", op: "function", attributes: {} },
async () => {
try {
return await db.roles.findUnique({
where: { id }
});
} catch (error) {
this.crashReporterService.report(error);
throw error;
}
}
)
}
async getByName(name: string): Promise<IRolesSchema | null> {
return this.instrumentationService.startSpan(
{ name: "Find Role By Name", op: "function", attributes: {} },
async () => {
try {
return await db.roles.findUnique({
where: { name }
});
} catch (error) {
this.crashReporterService.report(error);
throw error;
}
}
)
}
async getAll(): Promise<IRolesSchema[]> {
return this.instrumentationService.startSpan(
{ name: "Find All Roles", op: "function", attributes: {} },
async () => {
try {
return await db.roles.findMany();
} catch (error) {
this.crashReporterService.report(error);
throw error;
}
}
)
}
async create(data: ICreateRoleSchema): Promise<IRolesSchema> {
return this.instrumentationService.startSpan(
{ name: "Create Role", op: "function", attributes: {} },
async () => {
try {
return await db.roles.create({
data: {
name: data.name,
description: data.description,
}
});
} catch (error) {
this.crashReporterService.report(error);
throw error;
}
}
)
}
async update(id: string, data: ICreateRoleSchema): Promise<IRolesSchema> {
return this.instrumentationService.startSpan(
{ name: "Update Role", op: "function", attributes: {} },
async () => {
try {
return await db.roles.update({
where: { id },
data: {
name: data.name,
description: data.description,
}
});
} catch (error) {
this.crashReporterService.report(error);
throw error;
}
}
)
}
async delete(id: string): Promise<IRolesSchema> {
return this.instrumentationService.startSpan(
{ name: "Delete Role", op: "function", attributes: {} },
async () => {
try {
return await db.roles.delete({
where: { id }
});
} catch (error) {
this.crashReporterService.report(error);
throw error;
}
}
)
}
}

View File

@ -3,7 +3,7 @@ import { ICrashReporterService } from "@/src/application/services/crash-reporter
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface"; import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
import { createAdminClient } from "@/app/_utils/supabase/admin"; import { createAdminClient } from "@/app/_utils/supabase/admin";
import { createClient as createServerClient } from "@/app/_utils/supabase/server"; import { createClient as createServerClient } from "@/app/_utils/supabase/server";
import { IUserSchema } from "@/src/entities/models/users/users.model"; import { IUserSchema, IUserSupabaseSchema } from "@/src/entities/models/users/users.model";
import { ITransaction } from "@/src/entities/models/transaction.interface"; import { ITransaction } from "@/src/entities/models/transaction.interface";
import db from "@/prisma/db"; import db from "@/prisma/db";
import { DatabaseOperationError, NotFoundError } from "@/src/entities/errors/common"; import { DatabaseOperationError, NotFoundError } from "@/src/entities/errors/common";
@ -15,6 +15,7 @@ import { ICredentialsDeleteUserSchema } from "@/src/entities/models/users/delete
import { IBanUserSchema, ICredentialsBanUserSchema } from "@/src/entities/models/users/ban-user.model"; import { IBanUserSchema, ICredentialsBanUserSchema } from "@/src/entities/models/users/ban-user.model";
import { ICredentialsUnbanUserSchema } from "@/src/entities/models/users/unban-user.model"; import { ICredentialsUnbanUserSchema } from "@/src/entities/models/users/unban-user.model";
import { ICreateUserSchema } from "@/src/entities/models/users/create-user.model"; import { ICreateUserSchema } from "@/src/entities/models/users/create-user.model";
import { User } from "@supabase/supabase-js";
export class UsersRepository implements IUsersRepository { export class UsersRepository implements IUsersRepository {
constructor( constructor(
@ -33,6 +34,7 @@ export class UsersRepository implements IUsersRepository {
const query = db.users.findMany({ const query = db.users.findMany({
include: { include: {
profile: true, profile: true,
role: true,
}, },
}); });
@ -69,6 +71,7 @@ export class UsersRepository implements IUsersRepository {
}, },
include: { include: {
profile: true, profile: true,
role: true,
}, },
}) })
@ -88,7 +91,6 @@ export class UsersRepository implements IUsersRepository {
return { return {
...user, ...user,
id: credential.id,
}; };
} catch (err) { } catch (err) {
this.crashReporterService.report(err); this.crashReporterService.report(err);
@ -110,6 +112,7 @@ export class UsersRepository implements IUsersRepository {
}, },
include: { include: {
profile: true, profile: true,
role: true,
}, },
}) })
@ -149,6 +152,7 @@ export class UsersRepository implements IUsersRepository {
}, },
include: { include: {
profile: true, profile: true,
role: true,
}, },
}) })
@ -207,6 +211,7 @@ export class UsersRepository implements IUsersRepository {
}, },
include: { include: {
profile: true, profile: true,
role: true,
}, },
}); });
@ -215,11 +220,7 @@ export class UsersRepository implements IUsersRepository {
} }
return { return {
...user, ...userDetail
profile: {
user_id: userDetail.id,
...userDetail.profile,
},
}; };
} catch (err) { } catch (err) {
this.crashReporterService.report(err); this.crashReporterService.report(err);
@ -228,7 +229,7 @@ export class UsersRepository implements IUsersRepository {
}) })
} }
async createUser(input: ICreateUserSchema, tx?: ITransaction): Promise<IUserSchema> { async createUser(input: ICreateUserSchema, tx?: ITransaction): Promise<IUserSupabaseSchema> {
return await this.instrumentationService.startSpan({ return await this.instrumentationService.startSpan({
name: "UsersRepository > createUser", name: "UsersRepository > createUser",
}, async () => { }, async () => {
@ -241,10 +242,11 @@ export class UsersRepository implements IUsersRepository {
const query = supabase.auth.admin.createUser({ const query = supabase.auth.admin.createUser({
email: input.email, email: input.email,
password: input.password, password: input.password,
email_confirm: true, email_confirm: input.email_confirm ?? true,
phone: input.phone,
}) })
const { data: { user } } = await this.instrumentationService.startSpan({ const { data: { user }, error } = await this.instrumentationService.startSpan({
name: "UsersRepository > createUser > supabase.auth.admin.createUser", name: "UsersRepository > createUser > supabase.auth.admin.createUser",
op: "db:query", op: "db:query",
attributes: { "system": "supabase.auth" }, attributes: { "system": "supabase.auth" },
@ -254,7 +256,7 @@ export class UsersRepository implements IUsersRepository {
} }
) )
if (!user) { if (error || !user) {
throw new DatabaseOperationError("Failed to create user"); throw new DatabaseOperationError("Failed to create user");
} }
@ -267,7 +269,7 @@ export class UsersRepository implements IUsersRepository {
}) })
} }
async inviteUser(credential: ICredentialsInviteUserSchema, tx?: ITransaction): Promise<IUserSchema> { async inviteUser(credential: ICredentialsInviteUserSchema, tx?: ITransaction): Promise<IUserSupabaseSchema> {
return await this.instrumentationService.startSpan({ return await this.instrumentationService.startSpan({
name: "UsersRepository > inviteUser", name: "UsersRepository > inviteUser",
}, async () => { }, async () => {
@ -335,6 +337,7 @@ export class UsersRepository implements IUsersRepository {
id: credential.id, id: credential.id,
}, },
include: { include: {
role: true,
profile: true, profile: true,
}, },
}) })
@ -357,8 +360,12 @@ export class UsersRepository implements IUsersRepository {
where: { where: {
id: credential.id, id: credential.id,
}, },
include: {
profile: true,
role: true,
},
data: { data: {
role: input.role || user.role, roles_id: input.roles_id || user.roles_id,
invited_at: input.invited_at || user.invited_at, invited_at: input.invited_at || user.invited_at,
confirmed_at: input.confirmed_at || user.confirmed_at, confirmed_at: input.confirmed_at || user.confirmed_at,
last_sign_in_at: input.last_sign_in_at || user.last_sign_in_at, last_sign_in_at: input.last_sign_in_at || user.last_sign_in_at,
@ -377,9 +384,6 @@ export class UsersRepository implements IUsersRepository {
}, },
}, },
}, },
include: {
profile: true,
},
}) })
const updatedUser = await this.instrumentationService.startSpan({ const updatedUser = await this.instrumentationService.startSpan({

View File

@ -1,6 +1,7 @@
import { createAdminClient } from "@/app/_utils/supabase/admin"; import { createAdminClient } from "@/app/_utils/supabase/admin";
import { createClient } from "@/app/_utils/supabase/server"; import { createClient } from "@/app/_utils/supabase/server";
import db from "@/prisma/db"; import db from "@/prisma/db";
import { IPermissionsRepository } from "@/src/application/repositories/permissions.repository.interface";
import { IUsersRepository } from "@/src/application/repositories/users.repository.interface"; import { IUsersRepository } from "@/src/application/repositories/users.repository.interface";
import { IAuthenticationService } from "@/src/application/services/authentication.service.interface"; import { IAuthenticationService } from "@/src/application/services/authentication.service.interface";
import { ICrashReporterService } from "@/src/application/services/crash-reporter.service.interface"; import { ICrashReporterService } from "@/src/application/services/crash-reporter.service.interface";
@ -19,6 +20,7 @@ export class AuthenticationService implements IAuthenticationService {
private readonly usersRepository: IUsersRepository, private readonly usersRepository: IUsersRepository,
private readonly instrumentationService: IInstrumentationService, private readonly instrumentationService: IInstrumentationService,
private readonly crashReporterService: ICrashReporterService, private readonly crashReporterService: ICrashReporterService,
private readonly permissionRepository: IPermissionsRepository,
private readonly supabaseAdmin = createAdminClient(), private readonly supabaseAdmin = createAdminClient(),
private readonly supabaseServer = createClient() private readonly supabaseServer = createClient()
) { } ) { }
@ -274,4 +276,30 @@ export class AuthenticationService implements IAuthenticationService {
} }
}) })
} }
async checkPermission(userId: string, action: string, resource: string): Promise<boolean> {
return await this.instrumentationService.startSpan({
name: "checkPermission Use Case",
}, async () => {
try {
const user = await db.users.findUnique({
where: { id: userId },
include: { role: true }
});
if (!user) {
return false;
}
const role = user.role.name;
return await this.permissionRepository.checkPermission(role, action, resource);
} catch (err) {
this.crashReporterService.report(err)
throw err
}
})
}
} }

View File

@ -0,0 +1,36 @@
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
import { ICheckPermissionsUseCase } from "@/src/application/use-cases/auth/check-permissions.use-case";
import { InputParseError } from "@/src/entities/errors/common";
import { z } from "zod";
const checkPermissionInputSchema = z.object({
userId: z.string().uuid("Please enter a valid user ID"),
action: z.string().nonempty("Please enter an action"),
resource: z.string().nonempty("Please enter a resource"),
})
export type ICheckPermissionsController = ReturnType<typeof checkPermissionsController>
export const checkPermissionsController =
(
instrumentationService: IInstrumentationService,
checkPermissionUseCase: ICheckPermissionsUseCase
) =>
async (input: Partial<z.infer<typeof checkPermissionInputSchema>>) => {
return await instrumentationService.startSpan({ name: "checkPermission Controller" },
async () => {
const { data, error: inputParseError } = checkPermissionInputSchema.safeParse(input)
if (inputParseError) {
throw new InputParseError("Invalid data", { cause: inputParseError })
}
return await checkPermissionUseCase(
data.userId,
data.action,
data.resource
)
}
)
}

View File

@ -0,0 +1,38 @@
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
import { ICreatePermissionUseCase } from "@/src/application/use-cases/permissions/create-permissions.use-case";
import { IGetCurrentUserUseCase } from "@/src/application/use-cases/users/get-current-user.use-case";
import { UnauthenticatedError } from "@/src/entities/errors/auth";
import { InputParseError } from "@/src/entities/errors/common";
import { CreatePermissionSchema } from "@/src/entities/models/permissions/create-permission.model";
import { z } from "zod";
const inputSchema = z.object({
action: z.string().min(1, { message: "Action is required" }),
resource_id: z.string().min(1, { message: "Resource ID is required" }),
role_id: z.string().min(1, { message: "Role ID is required" }),
})
export type ICreatePermissionController = ReturnType<typeof createPermissionController>;
export const createPermissionController = (
instrumentationService: IInstrumentationService,
createPermissionUseCase: ICreatePermissionUseCase,
getCurrentUserUseCase: IGetCurrentUserUseCase
) => async (input: Partial<z.infer<typeof inputSchema>>) => {
return await instrumentationService.startSpan({ name: "Create Permission Controller" }, async () => {
const session = await getCurrentUserUseCase();
if (!session) {
throw new UnauthenticatedError("Must be logged in to create a permission");
}
const { data, error: inputParseError } = inputSchema.safeParse(input);
if (inputParseError) {
throw new InputParseError('Invalid data', { cause: inputParseError });
}
return await createPermissionUseCase(data);
});
};

View File

@ -0,0 +1,23 @@
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
import { IDeletePermissionUseCase } from "@/src/application/use-cases/permissions/delete-permissions.use-case";
import { IGetCurrentUserUseCase } from "@/src/application/use-cases/users/get-current-user.use-case";
import { UnauthenticatedError } from "@/src/entities/errors/auth";
export type IDeletePermissionController = ReturnType<typeof deletePermissionController>;
export const deletePermissionController = (
instrumentationService: IInstrumentationService,
deletePermissionUseCase: IDeletePermissionUseCase,
getCurrentUserUseCase: IGetCurrentUserUseCase
) => async (id: string) => {
return await instrumentationService.startSpan({ name: "Delete Permission Controller" }, async () => {
const session = await getCurrentUserUseCase();
if (!session) {
throw new UnauthenticatedError("Must be logged in to delete a permission");
}
return await deletePermissionUseCase({ id });
});
};

View File

@ -0,0 +1,23 @@
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
import { IGetAllPermissionsUseCase } from "@/src/application/use-cases/permissions/get-all-permissions";
import { IGetCurrentUserUseCase } from "@/src/application/use-cases/users/get-current-user.use-case";
import { UnauthenticatedError } from "@/src/entities/errors/auth";
export type IGetAllPermissionsController = ReturnType<typeof getAllPermissionsController>;
export const getAllPermissionsController = (
instrumentationService: IInstrumentationService,
getAllPermissionsUseCase: IGetAllPermissionsUseCase,
getCurrentUserUseCase: IGetCurrentUserUseCase
) => async () => {
return await instrumentationService.startSpan({ name: "getAllPermissions Controller" }, async () => {
const session = await getCurrentUserUseCase();
if (!session) {
throw new UnauthenticatedError("Must be logged in to fetch permissions");
}
return await getAllPermissionsUseCase();
});
};

View File

@ -0,0 +1,34 @@
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
import { IGetPermissionByIdUseCase } from "@/src/application/use-cases/permissions/get-permissions-by-id.use-case";
import { IGetCurrentUserUseCase } from "@/src/application/use-cases/users/get-current-user.use-case";
import { UnauthenticatedError } from "@/src/entities/errors/auth";
import { InputParseError } from "@/src/entities/errors/common";
import { z } from "zod";
const inputSchema = z.object({
id: z.string().min(1, { message: "Permission ID is required" }),
});
export type IGetPermissionByIdController = ReturnType<typeof getPermissionByIdController>;
export const getPermissionByIdController = (
instrumentationService: IInstrumentationService,
getPermissionByIdUseCase: IGetPermissionByIdUseCase,
getCurrentUserUseCase: IGetCurrentUserUseCase
) => async (input: z.infer<typeof inputSchema>) => {
return await instrumentationService.startSpan({ name: "Get Permission By Id Controller" }, async () => {
const session = await getCurrentUserUseCase();
if (!session) {
throw new UnauthenticatedError("Must be logged in to view a permission");
}
const { data, error: inputParseError } = inputSchema.safeParse(input);
if (inputParseError) {
throw new InputParseError("Invalid data", { cause: inputParseError });
}
return await getPermissionByIdUseCase(data.id);
});
};

View File

@ -0,0 +1,35 @@
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
import { IGetPermissionByRoleAndResourcesUseCase } from "@/src/application/use-cases/permissions/get-permissions-by-role-and-resources.use-case";
import { IGetCurrentUserUseCase } from "@/src/application/use-cases/users/get-current-user.use-case";
import { UnauthenticatedError } from "@/src/entities/errors/auth";
import { InputParseError } from "@/src/entities/errors/common";
import { z } from "zod";
const inputSchema = z.object({
roleId: z.string(),
resourceId: z.string(),
})
export type IGetPermissionByRoleAndResourceController = ReturnType<typeof getPermissionByRoleAndResourceController>;
export const getPermissionByRoleAndResourceController = (
instrumentationService: IInstrumentationService,
getPermissionByRoleAndResourceUseCase: IGetPermissionByRoleAndResourcesUseCase,
getCurrentUserUseCase: IGetCurrentUserUseCase
) => async (input: z.infer<typeof inputSchema>) => {
return await instrumentationService.startSpan({ name: "Get Permissions Controller" }, async () => {
const session = await getCurrentUserUseCase();
if (!session) {
throw new UnauthenticatedError("Must be logged in to view permissions");
}
const { data, error: inputParseError } = inputSchema.safeParse(input);
if (inputParseError) {
throw new InputParseError("Invalid data", { cause: inputParseError });
}
return await getPermissionByRoleAndResourceUseCase(data.roleId, data.resourceId);
});
};

View File

@ -0,0 +1,23 @@
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
import { IGetPermissionByRoleUseCase } from "@/src/application/use-cases/permissions/get-permissions-by-role.use-case";
import { IGetCurrentUserUseCase } from "@/src/application/use-cases/users/get-current-user.use-case";
import { UnauthenticatedError } from "@/src/entities/errors/auth";
export type IGetPermissionByRoleController = ReturnType<typeof getPermissionByRoleController>;
export const getPermissionByRoleController = (
instrumentationService: IInstrumentationService,
getPermissionByRoleUseCase: IGetPermissionByRoleUseCase,
getCurrentUserUseCase: IGetCurrentUserUseCase
) => async (roleId: string) => {
return await instrumentationService.startSpan({ name: "Get Permission By Role Controller" }, async () => {
const session = await getCurrentUserUseCase();
if (!session) {
throw new UnauthenticatedError("Must be logged in to fetch permissions");
}
return await getPermissionByRoleUseCase(roleId);
});
};

View File

@ -0,0 +1,37 @@
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
import { IUpdatePermissionUseCase } from "@/src/application/use-cases/permissions/update-permissions.use-case";
import { IGetCurrentUserUseCase } from "@/src/application/use-cases/users/get-current-user.use-case";
import { UnauthenticatedError } from "@/src/entities/errors/auth";
import { InputParseError } from "@/src/entities/errors/common";
import { UpdatePermissionSchema } from "@/src/entities/models/permissions/update-permission.model";
import { z } from "zod";
const inputSchema = z.object({
action: z.string().min(1, { message: "Action is required" }),
resource_id: z.string().min(1, { message: "Resource ID is required" }),
role_id: z.string().min(1, { message: "Role ID is required" }),
});
export type IUpdatePermissionController = ReturnType<typeof updatePermissionController>;
export const updatePermissionController = (
instrumentationService: IInstrumentationService,
updatePermissionUseCase: IUpdatePermissionUseCase,
getCurrentUserUseCase: IGetCurrentUserUseCase
) => async (id: string, input: Partial<z.infer<typeof inputSchema>>) => {
return await instrumentationService.startSpan({ name: "Update Permission Controller" }, async () => {
const session = await getCurrentUserUseCase();
if (!session) {
throw new UnauthenticatedError("Must be logged in to update a permission");
}
const { data, error: inputParseError } = inputSchema.safeParse(input);
if (inputParseError) {
throw new InputParseError("Invalid data", { cause: inputParseError });
}
return await updatePermissionUseCase(id, data);
});
};

View File

@ -0,0 +1,36 @@
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
import { ICreateResourceUseCase } from "@/src/application/use-cases/resources/create-resources.use-case";
import { IGetCurrentUserUseCase } from "@/src/application/use-cases/users/get-current-user.use-case";
import { UnauthenticatedError } from "@/src/entities/errors/auth";
import { InputParseError } from "@/src/entities/errors/common";
import { z } from "zod";
const inputSchema = z.object({
name: z.string().min(1, { message: "Resource name is required" }),
description: z.string().optional(),
});
export type ICreateResourceController = ReturnType<typeof createResourceController>;
export const createResourceController = (
instrumentationService: IInstrumentationService,
createResourceUseCase: ICreateResourceUseCase,
getCurrentUserUseCase: IGetCurrentUserUseCase
) => async (input: Partial<z.infer<typeof inputSchema>>) => {
return await instrumentationService.startSpan({ name: "createResource Controller" }, async () => {
const session = await getCurrentUserUseCase();
if (!session) {
throw new UnauthenticatedError("Must be logged in to create a resource");
}
const { data, error: inputParseError } = inputSchema.safeParse(input);
if (inputParseError) {
throw new InputParseError("Invalid data", { cause: inputParseError });
}
return await createResourceUseCase(data);
});
};

View File

@ -0,0 +1,22 @@
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
import { IDeleteResourceUseCase } from "@/src/application/use-cases/resources/delete-resource.use-case";
import { IGetCurrentUserUseCase } from "@/src/application/use-cases/users/get-current-user.use-case";
import { UnauthenticatedError } from "@/src/entities/errors/auth";
export type IDeleteResourceController = ReturnType<typeof deleteResourceController>;
export const deleteResourceController = (
instrumentationService: IInstrumentationService,
deleteResourceUseCase: IDeleteResourceUseCase,
getCurrentUserUseCase: IGetCurrentUserUseCase
) => async (id: string) => {
return await instrumentationService.startSpan({ name: "deleteResource Controller" }, async () => {
const session = await getCurrentUserUseCase();
if (!session) {
throw new UnauthenticatedError("Must be logged in to delete a resource");
}
return await deleteResourceUseCase(id);
});
};

View File

@ -0,0 +1,34 @@
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
import { IGetResourceByIdUseCase } from "@/src/application/use-cases/resources/get-resource-by-id.use-case";
import { IGetCurrentUserUseCase } from "@/src/application/use-cases/users/get-current-user.use-case";
import { UnauthenticatedError } from "@/src/entities/errors/auth";
import { InputParseError } from "@/src/entities/errors/common";
import { z } from "zod";
const inputSchema = z.object({
id: z.string().min(1, { message: "Resource ID is required" }),
});
export type IGetResourceByIdController = ReturnType<typeof getResourceByIdController>;
export const getResourceByIdController = (
instrumentationService: IInstrumentationService,
getResourceById: IGetResourceByIdUseCase,
getCurrentUserUseCase: IGetCurrentUserUseCase
) => async (input: z.infer<typeof inputSchema>) => {
return await instrumentationService.startSpan({ name: "getResourceById Controller" }, async () => {
const session = await getCurrentUserUseCase();
if (!session) {
throw new UnauthenticatedError("Must be logged in to fetch a resource");
}
const { data, error: inputParseError } = inputSchema.safeParse(input);
if (inputParseError) {
throw new InputParseError("Invalid data", { cause: inputParseError });
}
return await getResourceById(data.id);
});
};

View File

@ -0,0 +1,33 @@
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
import { IGetResourcesByTypeUseCase } from "@/src/application/use-cases/resources/get-resources-by-type.use-case";
import { IGetCurrentUserUseCase } from "@/src/application/use-cases/users/get-current-user.use-case";
import { UnauthenticatedError } from "@/src/entities/errors/auth";
import { z } from "zod";
const inputSchema = z.object({
type: z.string().nonempty("Type is required"),
})
export type IGetResourcesByTypeController = ReturnType<typeof getResourcesByTypeController>;
export const getResourcesByTypeController = (
instrumentationService: IInstrumentationService,
getResourcesByTypeUseCase: IGetResourcesByTypeUseCase,
getCurrentUserUseCase: IGetCurrentUserUseCase
) => async (input: z.infer<typeof inputSchema>) => {
return await instrumentationService.startSpan({ name: "getResourcesByType Controller" }, async () => {
const session = await getCurrentUserUseCase();
if (!session) {
throw new UnauthenticatedError("Must be logged in to fetch resources");
}
const { data, error: inputParseError } = inputSchema.safeParse(input);
if (inputParseError) {
throw new Error("Invalid data", { cause: inputParseError });
}
return await getResourcesByTypeUseCase(data.type);
});
};

View File

@ -0,0 +1,35 @@
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
import { IUpdateResourceUseCase } from "@/src/application/use-cases/resources/update-resource.use-case";
import { IGetCurrentUserUseCase } from "@/src/application/use-cases/users/get-current-user.use-case";
import { UnauthenticatedError } from "@/src/entities/errors/auth";
import { InputParseError } from "@/src/entities/errors/common";
import { z } from "zod";
const inputSchema = z.object({
name: z.string().min(1, { message: "Resource name is required" }),
description: z.string().optional(),
});
export type IUpdateResourceController = ReturnType<typeof updateResourceController>;
export const updateResourceController = (
instrumentationService: IInstrumentationService,
updateResourceUseCase: IUpdateResourceUseCase,
getCurrentUserUseCase: IGetCurrentUserUseCase
) => async (id: string, input: Partial<z.infer<typeof inputSchema>>) => {
return await instrumentationService.startSpan({ name: "updateResource Controller" }, async () => {
const session = await getCurrentUserUseCase();
if (!session) {
throw new UnauthenticatedError("Must be logged in to update a resource");
}
const { data, error: inputParseError } = inputSchema.safeParse(input);
if (inputParseError) {
throw new InputParseError("Invalid data", { cause: inputParseError });
}
return await updateResourceUseCase(id, data);
});
};

View File

@ -0,0 +1,36 @@
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
import { ICreateRoleUseCase } from "@/src/application/use-cases/roles/create-role.use-case";
import { IGetCurrentUserUseCase } from "@/src/application/use-cases/users/get-current-user.use-case";
import { UnauthenticatedError } from "@/src/entities/errors/auth";
import { InputParseError } from "@/src/entities/errors/common";
import { z } from "zod";
const inputSchema = z.object({
name: z.string().min(1, { message: "Role name is required" }),
description: z.string().optional(),
});
export type ICreateRoleController = ReturnType<typeof createRoleController>;
export const createRoleController = (
instrumentationService: IInstrumentationService,
createRoleUseCase: ICreateRoleUseCase,
getCurrentUserUseCase: IGetCurrentUserUseCase
) => async (input: Partial<z.infer<typeof inputSchema>>) => {
return await instrumentationService.startSpan({ name: "createRole Controller" }, async () => {
const session = await getCurrentUserUseCase();
if (!session) {
throw new UnauthenticatedError("Must be logged in to create a role");
}
const { data, error: inputParseError } = inputSchema.safeParse(input);
if (inputParseError) {
throw new InputParseError("Invalid data", { cause: inputParseError });
}
return await createRoleUseCase(data);
});
};

View File

@ -0,0 +1,22 @@
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
import { IDeleteRoleUseCase } from "@/src/application/use-cases/roles/delete-role.use-case";
import { IGetCurrentUserUseCase } from "@/src/application/use-cases/users/get-current-user.use-case";
import { UnauthenticatedError } from "@/src/entities/errors/auth";
export type IDeleteRoleController = ReturnType<typeof deleteRoleController>;
export const deleteRoleController = (
instrumentationService: IInstrumentationService,
deleteRoleUseCase: IDeleteRoleUseCase,
getCurrentUserUseCase: IGetCurrentUserUseCase
) => async (id: string) => {
return await instrumentationService.startSpan({ name: "deleteRole Controller" }, async () => {
const session = await getCurrentUserUseCase();
if (!session) {
throw new UnauthenticatedError("Must be logged in to delete a role");
}
return await deleteRoleUseCase(id);
});
};

View File

@ -0,0 +1,36 @@
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
import { IGetRoleByIdUseCase } from "@/src/application/use-cases/roles/get-role-by-id.use-case";
import { IGetCurrentUserUseCase } from "@/src/application/use-cases/users/get-current-user.use-case";
import { UnauthenticatedError } from "@/src/entities/errors/auth";
import { InputParseError } from "@/src/entities/errors/common";
import { z } from "zod";
const inputSchema = z.object({
id: z.string()
})
export type IGetRoleByIdController = ReturnType<typeof getRoleByIdController>;
export const getRoleByIdController = (
instrumentationService: IInstrumentationService,
getRoleById: IGetRoleByIdUseCase,
getCurrentUserUseCase: IGetCurrentUserUseCase
) => async (input: Partial<z.infer<typeof inputSchema>>) => {
return await instrumentationService.startSpan({ name: "getRole Controller" }, async () => {
const session = await getCurrentUserUseCase();
if (!session) {
throw new UnauthenticatedError("Must be logged in to fetch a role");
}
const { data, error: inputParseError } = inputSchema.safeParse(input);
if (inputParseError) {
throw new InputParseError("Invalid data", { cause: inputParseError });
}
return await getRoleById(data.id);
});
};

View File

@ -0,0 +1,36 @@
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
import { IUpdateRoleUseCase } from "@/src/application/use-cases/roles/update-role.use-case";
import { IGetCurrentUserUseCase } from "@/src/application/use-cases/users/get-current-user.use-case";
import { UnauthenticatedError } from "@/src/entities/errors/auth";
import { InputParseError } from "@/src/entities/errors/common";
import { z } from "zod";
const inputSchema = z.object({
name: z.string().min(1, { message: "Role name is required" }),
description: z.string().optional(),
});
export type IUpdateRoleController = ReturnType<typeof updateRoleController>;
export const updateRoleController = (
instrumentationService: IInstrumentationService,
updateRoleUseCase: IUpdateRoleUseCase,
getCurrentUserUseCase: IGetCurrentUserUseCase
) => async (id: string, input: Partial<z.infer<typeof inputSchema>>) => {
return await instrumentationService.startSpan({ name: "updateRole Controller" }, async () => {
const session = await getCurrentUserUseCase();
if (!session) {
throw new UnauthenticatedError("Must be logged in to update a role");
}
const { data, error: inputParseError } = inputSchema.safeParse(input);
if (inputParseError) {
throw new InputParseError("Invalid data", { cause: inputParseError });
}
return await updateRoleUseCase(id, data);
});
};