e.stopPropagation()}>
@@ -50,19 +59,24 @@ export const ActionsCell: React.FC = ({ user, onUpdate }) => {
+ {isAllowedToUpdate && (
onUpdate(user)}>
Update
- {
- setSelectedUser({ id: user.id, email: user.email! })
- setDeleteDialogOpen(true)
- }}
- >
-
- Delete
-
+ )}
+ {isAllowedToDelete && (
+ {
+ setSelectedUser({ id: user.id, email: user.email! })
+ setDeleteDialogOpen(true)
+ }}
+ >
+
+ Delete
+
+ )}
+ {isAllowedToUpdate && user.banned_until != null && (
{
if (user.banned_until != null) {
@@ -77,6 +91,7 @@ export const ActionsCell: React.FC = ({ user, onUpdate }) => {
{user.banned_until != null ? "Unban" : "Ban"}
+ )}
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/user-management/_components/table/columns/actions-column.tsx b/sigap-website/app/(pages)/(admin)/dashboard/user-management/_components/table/columns/actions-column.tsx
index 77c524d..2e1e373 100644
--- a/sigap-website/app/(pages)/(admin)/dashboard/user-management/_components/table/columns/actions-column.tsx
+++ b/sigap-website/app/(pages)/(admin)/dashboard/user-management/_components/table/columns/actions-column.tsx
@@ -1,5 +1,5 @@
// columns/actions-column.tsx
-"use client"
+
import React from "react"
import { IUserSchema } from "@/src/entities/models/users/users.model"
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/user-management/_components/table/columns/index.ts b/sigap-website/app/(pages)/(admin)/dashboard/user-management/_components/table/columns/index.ts
index cfc9c50..3216fc7 100644
--- a/sigap-website/app/(pages)/(admin)/dashboard/user-management/_components/table/columns/index.ts
+++ b/sigap-website/app/(pages)/(admin)/dashboard/user-management/_components/table/columns/index.ts
@@ -1,5 +1,5 @@
// columns/index.ts
-"use client"
+
import type { ColumnDef } from "@tanstack/react-table"
import { IUserSchema, IUserFilterOptionsSchema } from "@/src/entities/models/users/users.model"
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/user-management/_components/table/filters/column-filter.tsx b/sigap-website/app/(pages)/(admin)/dashboard/user-management/_components/table/filters/column-filter.tsx
index 8421276..ea45b50 100644
--- a/sigap-website/app/(pages)/(admin)/dashboard/user-management/_components/table/filters/column-filter.tsx
+++ b/sigap-website/app/(pages)/(admin)/dashboard/user-management/_components/table/filters/column-filter.tsx
@@ -1,5 +1,5 @@
// filters/column-filter.tsx
-"use client"
+
import React from "react"
import { ListFilter } from "lucide-react"
diff --git a/sigap-website/app/(pages)/(admin)/dashboard/user-management/_components/user-management.tsx b/sigap-website/app/(pages)/(admin)/dashboard/user-management/_components/user-management.tsx
index f430c3f..4856947 100644
--- a/sigap-website/app/(pages)/(admin)/dashboard/user-management/_components/user-management.tsx
+++ b/sigap-website/app/(pages)/(admin)/dashboard/user-management/_components/user-management.tsx
@@ -1,43 +1,17 @@
"use client";
import { useState, useMemo, useEffect } from "react";
-import {
- PlusCircle,
- Search,
- MoreHorizontal,
- X,
- ChevronDown,
- UserPlus,
- Mail,
- ShieldAlert,
- ListFilter,
- Trash2,
- PenIcon as UserPen,
- ShieldCheck,
-} from "lucide-react";
-import { Button } from "@/app/_components/ui/button";
-import { Input } from "@/app/_components/ui/input";
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger,
-} from "@/app/_components/ui/dropdown-menu";
import { DataTable } from "../../../../../_components/data-table";
import { UserInformationSheet } from "./sheets/user-information-sheet";
import { useGetUsersQuery } from "../_queries/queries";
import { filterUsers, useUserManagementHandlers } from "../_handlers/use-user-management";
-import { UserDialogs } from "./dialogs/user-dialogs";
+
import { useAddUserDialogHandler } from "../_handlers/use-add-user-dialog";
import { useInviteUserHandler } from "../_handlers/use-invite-user";
import { AddUserDialog } from "./dialogs/add-user-dialog";
import { InviteUserDialog } from "./dialogs/invite-user-dialog";
-import { ConfirmDialog } from "@/app/_components/confirm-dialog";
-import { BanUserDialog } from "./dialogs/ban-user-dialog";
-import { useBanUserHandler } from "../_handlers/actions/use-ban-user";
-import { useDeleteUserHandler } from "../_handlers/actions/use-delete-user";
-import { useUnbanUserHandler } from "../_handlers/actions/use-unban-user";
+
import { createUserColumns } from "./table/columns";
import { UserManagementToolbar } from "./toolbars/user-management-toolbar";
import { UpdateUserSheet } from "./sheets/update-user-sheet";
diff --git a/sigap-website/app/(pages)/(auth)/_handlers/use-check-permissions.ts b/sigap-website/app/(pages)/(auth)/_handlers/use-check-permissions.ts
index 1df8673..d3d8c36 100644
--- a/sigap-website/app/(pages)/(auth)/_handlers/use-check-permissions.ts
+++ b/sigap-website/app/(pages)/(auth)/_handlers/use-check-permissions.ts
@@ -8,7 +8,7 @@ export const useCheckPermissionsHandler = () => {
}
return {
- handleCheckPermissions,
+ checkPermissions: handleCheckPermissions,
isPending,
}
}
\ No newline at end of file
diff --git a/sigap-website/app/(pages)/(auth)/_queries/mutations.ts b/sigap-website/app/(pages)/(auth)/_queries/mutations.ts
index 13fef9c..7fff4c2 100644
--- a/sigap-website/app/(pages)/(auth)/_queries/mutations.ts
+++ b/sigap-website/app/(pages)/(auth)/_queries/mutations.ts
@@ -1,51 +1,58 @@
-import { useMutation } from "@tanstack/react-query"
-import { checkPermissions, sendMagicLink, sendPasswordRecovery, signInPasswordless, signInWithPassword, signOut, verifyOtp } from "../action"
+import { useMutation, useQueries, useQuery } from "@tanstack/react-query"
+import { checkPermissionNew, checkPermissions, sendMagicLink, sendPasswordRecovery, signInPasswordless, signInWithPassword, signOut, verifyOtp } from "../action"
export const useSignInPasswordlessMutation = () => {
return useMutation({
- mutationKey: ["signIn"],
+ mutationKey: ["signin"],
mutationFn: async (formData: FormData) => await signInPasswordless(formData),
})
}
export const useSignInWithPasswordMutation = () => {
return useMutation({
- mutationKey: ["signInWithCredentials"],
+ mutationKey: ["signin", "credentials"],
mutationFn: async (formData: FormData) => await signInWithPassword(formData),
})
}
export const useSignOutMutation = () => {
return useMutation({
- mutationKey: ["signOut"],
+ mutationKey: ["signout"],
mutationFn: async () => await signOut(),
})
}
export const useSendMagicLinkMutation = () => {
return useMutation({
- mutationKey: ["sendMagicLink"],
+ mutationKey: ["send-magic-link"],
mutationFn: async (email: string) => await sendMagicLink(email),
})
}
export const useSendPasswordRecoveryMutation = () => {
return useMutation({
- mutationKey: ["sendPasswordRecovery"],
+ mutationKey: ["send-password-recovery"],
mutationFn: async (email: string) => await sendPasswordRecovery(email),
})
}
export const useVerifyOtpMutation = () => {
return useMutation({
- mutationKey: ["verifyOtp"],
+ mutationKey: ["verify-otp"],
mutationFn: async (formData: FormData) => await verifyOtp(formData),
})
}
export const useCheckPermissionsMutation = () => {
return useMutation({
- mutationKey: ["checkPermissions"],
+ mutationKey: ["check-permissions"],
mutationFn: async ({ userId, action, resource }: { userId: string; action: string; resource: string }) => await checkPermissions(userId, action, resource),
})
-}
\ No newline at end of file
+}
+
+export const useCheckPermissionsNewQuery = (email: string, action: string, resource: string) => {
+ return useQuery({
+ queryKey: ["check-permissions", email, action, resource],
+ queryFn: async () => await checkPermissionNew(email, action, resource),
+ })
+}
diff --git a/sigap-website/app/(pages)/(auth)/action.ts b/sigap-website/app/(pages)/(auth)/action.ts
index c61753c..145fa9e 100644
--- a/sigap-website/app/(pages)/(auth)/action.ts
+++ b/sigap-website/app/(pages)/(auth)/action.ts
@@ -7,6 +7,7 @@ import { revalidatePath } from "next/cache"
import { InputParseError, NotFoundError } from "@/src/entities/errors/common"
import { AuthenticationError, UnauthenticatedError } from "@/src/entities/errors/auth"
import { createClient } from "@/app/_utils/supabase/server"
+import db from "@/prisma/db";
export async function signInPasswordless(formData: FormData) {
const instrumentationService = getInjection("IInstrumentationService")
@@ -234,8 +235,77 @@ export async function checkPermissions(userId: string, action: string, resource:
recordResponse: true
}, async () => {
try {
- const checkPermissionsController = getInjection("ICheckPermissionsController")
- return await checkPermissionsController({ userId, action, resource })
+
+ const user = await db.users.findUnique({
+ where: { id: userId },
+ include: {
+ role: true
+ }
+ })
+
+ console.log("Checking permissions for user:", user?.role.name, "action:", action, "resource:", resource)
+
+ const permission = await db.permissions.findFirst({
+ where: {
+ role_id: user?.role.id,
+ action: action,
+ resource: {
+ name: resource
+ }
+ }
+ })
+
+ return !!permission
+
+ } 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.",
+ }
+ }
+ })
+}
+
+export async function checkPermissionNew(email: string, action: string, resource: string) {
+ const instrumentationService = getInjection("IInstrumentationService")
+ return await instrumentationService.instrumentServerAction("checkPermissionNew", {
+ recordResponse: true
+ }, async () => {
+ try {
+
+ const user = await db.users.findUnique({
+ where: { email },
+ include: { role: true }
+ })
+
+ if (!user) {
+ return { error: "User not found" }
+ }
+
+ console.log("Checking permissions for user:", user?.role.name, "action:", action, "resource:", resource)
+
+ const permission = await db.permissions.findFirst({
+ where: {
+ role_id: user.role.id,
+ action: action,
+ resource: {
+ name: resource
+ }
+ }
+ })
+
+ if (!permission) {
+ return false
+ }
+
+ return !!permission
+
} catch (err) {
if (err instanceof InputParseError) {
return { error: err.message }
diff --git a/sigap-website/di/modules/authentication.module.ts b/sigap-website/di/modules/authentication.module.ts
index 52a2d79..3234494 100644
--- a/sigap-website/di/modules/authentication.module.ts
+++ b/sigap-website/di/modules/authentication.module.ts
@@ -96,6 +96,13 @@ export function createAuthenticationModule() {
DI_SYMBOLS.IUsersRepository,
]);
+ authenticationModule
+ .bind(DI_SYMBOLS.ICheckPermissionsUseCase)
+ .toHigherOrderFunction(signUpUseCase, [
+ DI_SYMBOLS.IInstrumentationService,
+ DI_SYMBOLS.IAuthenticationService,
+ ]);
+
// Controllers
authenticationModule
@@ -141,6 +148,15 @@ export function createAuthenticationModule() {
DI_SYMBOLS.ISendPasswordRecoveryUseCase,
]);
+ authenticationModule
+ .bind(DI_SYMBOLS.ICheckPermissionsController)
+ .toHigherOrderFunction(signUpUseCase, [
+ DI_SYMBOLS.IInstrumentationService,
+ DI_SYMBOLS.IAuthenticationService,
+ DI_SYMBOLS.IUsersRepository,
+ ]);
+
+
return authenticationModule;
}
\ No newline at end of file
diff --git a/sigap-website/di/types.ts b/sigap-website/di/types.ts
index 5358d33..ae1b37b 100644
--- a/sigap-website/di/types.ts
+++ b/sigap-website/di/types.ts
@@ -77,6 +77,7 @@ import { IDeleteResourceController } from '@/src/interface-adapters/controllers/
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';
+import { ISignInWithPasswordUseCase } from '@/src/application/use-cases/auth/sign-in-with-password.use-case';
// Pastikan DI_SYMBOLS memiliki tipe yang sesuai dengan DI_RETURN_TYPES
export const DI_SYMBOLS: { [K in keyof DI_RETURN_TYPES]: symbol } = {
@@ -96,6 +97,7 @@ export const DI_SYMBOLS: { [K in keyof DI_RETURN_TYPES]: symbol } = {
// Auth Use Cases
ISignInPasswordlessUseCase: Symbol.for('ISignInPasswordlessUseCase'),
+ ISignInWithPasswordUseCase: Symbol.for('ISignInWithPasswordUseCase'),
ISignUpUseCase: Symbol.for('ISignUpUseCase'),
IVerifyOtpUseCase: Symbol.for('IVerifyOtpUseCase'),
ISignOutUseCase: Symbol.for('ISignOutUseCase'),
@@ -204,6 +206,7 @@ export interface DI_RETURN_TYPES {
// Auth Use Cases
ISignInPasswordlessUseCase: ISignInPasswordlessUseCase;
+ ISignInWithPasswordUseCase: ISignInWithPasswordUseCase;
ISignUpUseCase: ISignUpUseCase;
IVerifyOtpUseCase: IVerifyOtpUseCase;
ISignOutUseCase: ISignOutUseCase;
diff --git a/sigap-website/src/application/repositories/users.repository.interface.ts b/sigap-website/src/application/repositories/users.repository.interface.ts
index 8aa9e03..0378ba6 100644
--- a/sigap-website/src/application/repositories/users.repository.interface.ts
+++ b/sigap-website/src/application/repositories/users.repository.interface.ts
@@ -19,8 +19,8 @@ export interface IUsersRepository {
createUser(input: ICreateUserSchema, tx?: ITransaction): Promise
;
inviteUser(credential: ICredentialsInviteUserSchema, tx?: ITransaction): Promise;
updateUser(credential: ICredentialUpdateUserSchema, input: Partial, tx?: ITransaction): Promise;
- deleteUser(credential: ICredentialsDeleteUserSchema, tx?: ITransaction): Promise;
- banUser(credential: ICredentialsBanUserSchema, input: IBanUserSchema, tx?: ITransaction): Promise;
- unbanUser(credential: ICredentialsUnbanUserSchema, tx?: ITransaction): Promise;
+ deleteUser(credential: ICredentialsDeleteUserSchema, tx?: ITransaction): Promise;
+ banUser(credential: ICredentialsBanUserSchema, input: IBanUserSchema, tx?: ITransaction): Promise;
+ unbanUser(credential: ICredentialsUnbanUserSchema, tx?: ITransaction): Promise;
uploadAvatar(userId: string, file: File): Promise;
}
\ No newline at end of file
diff --git a/sigap-website/src/entities/models/auth/session.model.ts b/sigap-website/src/entities/models/auth/session.model.ts
index bebd641..6cbbebd 100644
--- a/sigap-website/src/entities/models/auth/session.model.ts
+++ b/sigap-website/src/entities/models/auth/session.model.ts
@@ -2,10 +2,10 @@ import { z } from "zod";
import { UserSchema } from "@/src/entities/models/users/users.model";
export const SessionSchema = z.object({
- user: UserSchema.pick({
- id: true,
- email: true,
- roles_id: true,
+ user: z.object({
+ id: z.string(),
+ email: z.string().email().optional(),
+ role_id: z.string().optional(),
}),
expiresAt: z.number().optional(),
});
diff --git a/sigap-website/src/entities/models/users/users.model.ts b/sigap-website/src/entities/models/users/users.model.ts
index e409efd..dbff374 100644
--- a/sigap-website/src/entities/models/users/users.model.ts
+++ b/sigap-website/src/entities/models/users/users.model.ts
@@ -39,7 +39,7 @@ const timestampSchema = z.union([z.string(), z.date()]).nullable();
export const UserSchema = z.object({
id: z.string(),
roles_id: z.string().optional(), // Sesuaikan dengan field di Prisma
- email: z.string().email().optional(),
+ email: z.string().email(),
email_confirmed_at: z.union([z.string(), z.date()]).nullable().optional(),
encrypted_password: z.string().nullable().optional(),
invited_at: z.union([z.string(), z.date()]).nullable().optional(),
diff --git a/sigap-website/src/infrastructure/repositories/permissions.repository.ts b/sigap-website/src/infrastructure/repositories/permissions.repository.ts
index cc84023..97d99c2 100644
--- a/sigap-website/src/infrastructure/repositories/permissions.repository.ts
+++ b/sigap-website/src/infrastructure/repositories/permissions.repository.ts
@@ -69,18 +69,28 @@ export class PermissionsRepository implements IPermissionsRepository {
}
async checkPermission(role: string, action: string, resource: string): Promise {
- const result = await db.permissions.findFirst({
- where: {
- role: {
- name: role
- },
- action: action,
- resource: {
- name: resource
+ return await this.instrumentationService.startSpan({ name: "Check Permission" },
+ async () => {
+ try {
+
+ const result = await db.permissions.findFirst({
+ where: {
+ role: {
+ name: role
+ },
+ action: action,
+ resource: {
+ name: resource
+ }
+ }
+ });
+
+ return !!result;
+ } catch (err) {
+ this.crashReporterService.report(err);
+ throw err;
}
}
- });
-
- return !!result;
+ )
}
}
\ No newline at end of file
diff --git a/sigap-website/src/infrastructure/repositories/users.repository.ts b/sigap-website/src/infrastructure/repositories/users.repository.ts
index 75c8190..571adab 100644
--- a/sigap-website/src/infrastructure/repositories/users.repository.ts
+++ b/sigap-website/src/infrastructure/repositories/users.repository.ts
@@ -412,7 +412,7 @@ export class UsersRepository implements IUsersRepository {
})
}
- async deleteUser(credential: ICredentialsDeleteUserSchema, tx?: ITransaction): Promise {
+ async deleteUser(credential: ICredentialsDeleteUserSchema, tx?: ITransaction): Promise {
return await this.instrumentationService.startSpan({
name: "UsersRepository > deleteUser",
}, async () => {
@@ -435,10 +435,7 @@ export class UsersRepository implements IUsersRepository {
throw new DatabaseOperationError("Failed to delete user");
}
- return {
- ...user,
- id: credential.id,
- };
+ return;
} catch (err) {
this.crashReporterService.report(err);
@@ -447,7 +444,7 @@ export class UsersRepository implements IUsersRepository {
})
}
- async banUser(credential: ICredentialsBanUserSchema, input: IBanUserSchema, tx?: ITransaction): Promise {
+ async banUser(credential: ICredentialsBanUserSchema, input: IBanUserSchema, tx?: ITransaction): Promise {
return await this.instrumentationService.startSpan({
name: "UsersRepository > banUser",
}, async () => {
@@ -472,10 +469,7 @@ export class UsersRepository implements IUsersRepository {
throw new DatabaseOperationError("Failed to ban user");
}
- return {
- ...user,
- id: credential.id,
- };
+ return;
} catch (err) {
this.crashReporterService.report(err);
@@ -485,7 +479,7 @@ export class UsersRepository implements IUsersRepository {
}
- async unbanUser(credential: ICredentialsUnbanUserSchema, tx?: ITransaction): Promise {
+ async unbanUser(credential: ICredentialsUnbanUserSchema, tx?: ITransaction): Promise {
return await this.instrumentationService.startSpan({
name: "UsersRepository > unbanUser",
}, async () => {
@@ -510,10 +504,7 @@ export class UsersRepository implements IUsersRepository {
throw new DatabaseOperationError("Failed to unban user");
}
- return {
- ...user,
- id: credential.id,
- };
+ return;
} catch (err) {
this.crashReporterService.report(err);
diff --git a/sigap-website/src/infrastructure/services/authentication.service.ts b/sigap-website/src/infrastructure/services/authentication.service.ts
index a7817b4..dc778c4 100644
--- a/sigap-website/src/infrastructure/services/authentication.service.ts
+++ b/sigap-website/src/infrastructure/services/authentication.service.ts
@@ -279,22 +279,33 @@ export class AuthenticationService implements IAuthenticationService {
async checkPermission(userId: string, action: string, resource: string): Promise {
return await this.instrumentationService.startSpan({
- name: "checkPermission Use Case",
+ name: "",
}, async () => {
try {
- const user = await db.users.findUnique({
- where: { id: userId },
- include: { role: true }
- });
- if (!user) {
- return false;
- }
+ // const user = await db.users.findUnique({
+ // where: { id: userId },
+ // include: { role: true }
+ // });
- const role = user.role.name;
+ // if (!user) {
+ // return false;
+ // }
- return await this.permissionRepository.checkPermission(role, action, resource);
+ // const role = user.role.name;
+
+ // const permission = await db.permissions.findFirst({
+ // where: {
+ // role: { name: role },
+ // action,
+ // resource: {
+ // name: resource
+ // }
+ // }
+ // })
+
+ return true
} catch (err) {
this.crashReporterService.report(err)
diff --git a/sigap-website/src/interface-adapters/controllers/auth/check-permissions.controller.ts b/sigap-website/src/interface-adapters/controllers/auth/check-permissions.controller.ts
index 00cb9a1..700f923 100644
--- a/sigap-website/src/interface-adapters/controllers/auth/check-permissions.controller.ts
+++ b/sigap-website/src/interface-adapters/controllers/auth/check-permissions.controller.ts
@@ -1,3 +1,4 @@
+import { IUsersRepository } from "@/src/application/repositories/users.repository.interface";
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";
@@ -15,11 +16,19 @@ export type ICheckPermissionsController = ReturnType
async (input: Partial>) => {
return await instrumentationService.startSpan({ name: "checkPermission Controller" },
async () => {
+
+ // const session = await usersRpository.getCurrentUser()
+
+ // if (!session) {
+ // throw new InputParseError("User not found")
+ // }
+
const { data, error: inputParseError } = checkPermissionInputSchema.safeParse(input)
if (inputParseError) {