diff --git a/app/Http/Controllers/Api/Admin/Employee/EmployeeController.php b/app/Http/Controllers/Api/Admin/Employee/EmployeeController.php new file mode 100644 index 0000000..07ea1c3 --- /dev/null +++ b/app/Http/Controllers/Api/Admin/Employee/EmployeeController.php @@ -0,0 +1,170 @@ +employeeRepository->filter($request->all()); + + return response()->json([ + 'error' => false, + 'data' => $employees, + ]); + } + + public function store(EmployeeRequest $request): JsonResponse + { + $request->validated(); + + try { + DB::beginTransaction(); + + $employee = $this->employeeRepository->create([ + 'firstname' => $request->firstname, + 'lastname' => $request->lastname, + 'username' => $request->username, + 'email' => $request->email, + 'phone' => $request->phone, + 'birthday' => $request->birthday, + 'address' => $request->address, + 'password' => Hash::make($request->password), + ]); + + $employee->syncRoles([$request->role]); + + DB::commit(); + + return response()->json([ + 'error' => false, + 'message' => 'Employee created successfully', + 'data' => $employee->load('roles'), + ], 201); + } catch (\Exception $e) { + DB::rollBack(); + return response()->json([ + 'error' => true, + 'message' => $e->getMessage(), + ], 500); + } + } + + public function show(int $id): JsonResponse + { + $employee = $this->employeeRepository->find($id); + + if (!$employee) { + return response()->json([ + 'error' => true, + 'message' => "Can't find employee by ID #" . $id, + ], 404); + } + + return response()->json([ + 'error' => false, + 'data' => $employee->load('roles'), + ]); + } + + public function update(int $id, EmployeeRequest $request): JsonResponse + { + $employee = $this->employeeRepository->find($id); + + if (!$employee) { + return response()->json([ + 'error' => true, + 'message' => "Can't find employee by ID #" . $id, + ], 404); + } + + $request->validated(); + + try { + DB::beginTransaction(); + + $data = [ + 'firstname' => $request->firstname, + 'lastname' => $request->lastname, + 'username' => $request->username, + 'email' => $request->email, + 'phone' => $request->phone, + 'birthday' => $request->birthday, + 'address' => $request->address, + ]; + + if ($request->filled('password')) { + $data['password'] = Hash::make($request->password); + } + + $this->employeeRepository->update($id, $data); + $employee->syncRoles([$request->role]); + + DB::commit(); + + return response()->json([ + 'error' => false, + 'message' => 'Employee updated successfully', + 'data' => $this->employeeRepository->find($id)->load('roles'), + ]); + } catch (\Exception $e) { + DB::rollBack(); + return response()->json([ + 'error' => true, + 'message' => $e->getMessage(), + ], 500); + } + } + + public function destroy(int $id): JsonResponse + { + $employee = $this->employeeRepository->find($id); + + if (!$employee) { + return response()->json([ + 'error' => true, + 'message' => "Can't find employee by ID #" . $id, + ], 404); + } + + try { + DB::beginTransaction(); + $this->employeeRepository->delete($id); + DB::commit(); + + return response()->json([ + 'error' => false, + 'message' => 'Employee deleted successfully', + ]); + } catch (\Exception $e) { + DB::rollBack(); + return response()->json([ + 'error' => true, + 'message' => $e->getMessage(), + ], 500); + } + } + + public function roles(): JsonResponse + { + $roles = Role::where('guard_name', 'admin')->get(['id', 'name']); + + return response()->json([ + 'error' => false, + 'data' => $roles, + ]); + } +} diff --git a/app/Http/Controllers/Api/Admin/Role/RolePermissionController.php b/app/Http/Controllers/Api/Admin/Role/RolePermissionController.php new file mode 100644 index 0000000..e619c07 --- /dev/null +++ b/app/Http/Controllers/Api/Admin/Role/RolePermissionController.php @@ -0,0 +1,266 @@ +user(); + return $user && $user->hasRole('Full Administrator'); + } + + /** + * GET /v1/administrator/role-permissions + * Danh sách tất cả roles (trừ Full Administrator) kèm permissions hiện tại + */ + public function index(): JsonResponse + { + if (!$this->ensureFullAdmin()) { + return response()->json(['error' => true, 'message' => 'Bạn không có quyền truy cập'], 403); + } + + $roles = Role::where('guard_name', 'admin') + ->where('name', '!=', 'Full Administrator') + ->with('permissions') + ->get() + ->map(fn ($role) => [ + 'id' => $role->id, + 'name' => $role->name, + 'permissions' => $role->permissions->pluck('name')->values(), + ]); + + return response()->json([ + 'error' => false, + 'data' => $roles, + ]); + } + + /** + * GET /v1/administrator/role-permissions/all-permissions + * Tất cả permissions nhóm theo module + */ + public function allPermissions(): JsonResponse + { + if (!$this->ensureFullAdmin()) { + return response()->json(['error' => true, 'message' => 'Bạn không có quyền truy cập'], 403); + } + + $grouped = Permission::where('guard_name', 'admin') + ->orderBy('id') + ->get(['id', 'name']) + ->groupBy(function ($p) { + // Format: module.action (e.g., product-service.view, customer.manager) + $parts = explode('.', $p->name); + return $parts[0] ?? $p->name; + }) + ->map(function ($group, $module) { + return [ + 'module' => $module, + 'permissions' => $group->map(fn ($p) => [ + 'id' => $p->id, + 'name' => $p->name, + 'action' => explode('.', $p->name)[1] ?? $p->name, + ])->values(), + ]; + }) + ->values(); + + return response()->json([ + 'error' => false, + 'data' => $grouped, + ]); + } + + /** + * GET /v1/administrator/role-permissions/mine + * Trả về permissions hiệu lực của user đang đăng nhập (qua role của họ). + * Accessible bởi mọi admin đã xác thực — không yêu cầu Full Administrator. + */ + public function mine(): JsonResponse + { + $user = auth('admin')->user(); + if (!$user) { + return response()->json(['error' => true, 'message' => 'Unauthorized'], 401); + } + + $isFullAdmin = $user->hasRole('Full Administrator', 'admin'); + + return response()->json([ + 'error' => false, + 'data' => [ + 'isFullAdmin' => $isFullAdmin, + 'permissions' => $isFullAdmin + ? ['*'] + : $user->getAllPermissions()->pluck('name')->toArray(), + ], + ]); + } + + /** + * POST /v1/administrator/role-permissions/permissions + * Tạo một nhóm permissions mới theo module (chỉ Full Administrator) + */ + public function createPermissionGroup(Request $request): JsonResponse + { + if (!$this->ensureFullAdmin()) { + return response()->json(['error' => true, 'message' => 'Bạn không có quyền truy cập'], 403); + } + + $module = trim($request->input('module', '')); + $actions = $request->input('actions', ['view', 'create', 'edit', 'destroy', 'manager']); + + if (!$module) { + return response()->json(['error' => true, 'message' => 'Tên module không được trống'], 422); + } + + // Kiểm tra module đã tồn tại chưa + $existing = Permission::where('guard_name', 'admin') + ->where('name', 'like', $module . '.%') + ->exists(); + + if ($existing) { + return response()->json(['error' => true, 'message' => "Module \"{$module}\" đã tồn tại"], 422); + } + + $created = []; + foreach ($actions as $action) { + $name = $module . '.' . $action; + $perm = Permission::firstOrCreate([ + 'name' => $name, + 'guard_name' => 'admin', + ]); + $created[] = [ + 'id' => $perm->id, + 'name' => $perm->name, + 'action' => $action, + ]; + } + + // Full Administrator tự động được cấp permissions mới + $fullAdmin = Role::where('name', 'Full Administrator') + ->where('guard_name', 'admin') + ->first(); + if ($fullAdmin) { + $fullAdmin->givePermissionTo(array_column($created, 'name')); + } + + app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions(); + + return response()->json([ + 'error' => false, + 'data' => [ + 'module' => $module, + 'permissions' => $created, + ], + ], 201); + } + + /** + * DELETE /v1/administrator/role-permissions/permissions/group/{module} + * Xóa toàn bộ permissions của một module (chỉ Full Administrator) + */ + public function deletePermissionGroup(string $module): JsonResponse + { + if (!$this->ensureFullAdmin()) { + return response()->json(['error' => true, 'message' => 'Bạn không có quyền truy cập'], 403); + } + + $deleted = Permission::where('guard_name', 'admin') + ->where('name', 'like', $module . '.%') + ->get(); + + if ($deleted->isEmpty()) { + return response()->json(['error' => true, 'message' => 'Không tìm thấy nhóm quyền'], 404); + } + + foreach ($deleted as $perm) { + $perm->delete(); + } + + app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions(); + + return response()->json([ + 'error' => false, + 'message' => "Đã xóa nhóm quyền \"{$module}\"", + 'data' => ['module' => $module, 'deleted' => $deleted->count()], + ]); + } + + /** + * PATCH /v1/administrator/role-permissions/permissions/{id} + * Đổi tên một permission (chỉ Full Administrator) + */ + public function renamePermission(int $id, Request $request): JsonResponse + { + if (!$this->ensureFullAdmin()) { + return response()->json(['error' => true, 'message' => 'Bạn không có quyền truy cập'], 403); + } + + $permission = Permission::where('guard_name', 'admin')->find($id); + if (!$permission) { + return response()->json(['error' => true, 'message' => 'Không tìm thấy permission'], 404); + } + + $newName = trim($request->input('name', '')); + if (!$newName) { + return response()->json(['error' => true, 'message' => 'Tên permission không được trống'], 422); + } + + if (Permission::where('guard_name', 'admin')->where('name', $newName)->where('id', '!=', $id)->exists()) { + return response()->json(['error' => true, 'message' => 'Tên permission đã tồn tại'], 422); + } + + $permission->update(['name' => $newName]); + app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions(); + + return response()->json([ + 'error' => false, + 'data' => ['id' => $permission->id, 'name' => $permission->name], + ]); + } + + /** + * PUT /v1/administrator/role-permissions/{roleId} + * Sync permissions cho một role + */ + public function update(int $roleId, Request $request): JsonResponse + { + if (!$this->ensureFullAdmin()) { + return response()->json(['error' => true, 'message' => 'Bạn không có quyền truy cập'], 403); + } + + $role = Role::where('guard_name', 'admin') + ->where('name', '!=', 'Full Administrator') + ->find($roleId); + + if (!$role) { + return response()->json([ + 'error' => true, + 'message' => 'Không tìm thấy vai trò', + ], 404); + } + + $permissions = $request->input('permissions', []); + $role->syncPermissions($permissions); + + // Xóa cache để /mine endpoint trả về permissions mới nhất + app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions(); + + return response()->json([ + 'error' => false, + 'message' => "Đã cập nhật quyền cho vai trò \"{$role->name}\"", + 'data' => [ + 'id' => $role->id, + 'name' => $role->name, + 'permissions' => $role->permissions()->pluck('name')->values(), + ], + ]); + } +} diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 3560712..ce14713 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -70,5 +70,6 @@ class Kernel extends HttpKernel 'Staff' => \App\Http\Middleware\StaffCheck::class, 'json.response' => \App\Http\Middleware\ForceJsonResponse::class, 'api.auth' => \App\Http\Middleware\ApiAuthCheck::class, + 'perm' => \App\Http\Middleware\CheckPermission::class, ]; } diff --git a/app/Http/Middleware/CheckPermission.php b/app/Http/Middleware/CheckPermission.php new file mode 100644 index 0000000..340755e --- /dev/null +++ b/app/Http/Middleware/CheckPermission.php @@ -0,0 +1,35 @@ +user(); + + if (!$user) { + return response()->json([ + 'error' => true, + 'message' => 'Vui lòng đăng nhập để thực hiện!', + ], 401); + } + + // Full Administrator luôn có tất cả quyền + if ($user->hasRole('Full Administrator')) { + return $next($request); + } + + if (!$user->hasPermissionTo($permission)) { + return response()->json([ + 'error' => true, + 'message' => "Bạn không có quyền: \"{$permission}\".", + ], 403); + } + + return $next($request); + } +} diff --git a/app/Http/Requests/EmployeeRequest.php b/app/Http/Requests/EmployeeRequest.php new file mode 100644 index 0000000..7dbf307 --- /dev/null +++ b/app/Http/Requests/EmployeeRequest.php @@ -0,0 +1,38 @@ +route('id'); + + $rules = [ + 'firstname' => 'required|string|max:255', + 'lastname' => 'required|string|max:255', + 'username' => ['required', 'string', 'max:255', Rule::unique('user', 'username')->ignore($employeeId)], + 'email' => ['required', 'email', 'max:255', Rule::unique('user', 'email')->ignore($employeeId)], + 'role' => ['required', Rule::in(['Administrator', 'Sale', 'IT', 'Customer Service', 'Marketing'])], + 'phone' => 'nullable|string|max:20', + 'birthday' => 'nullable|date', + 'address' => 'nullable|string|max:500', + ]; + + if ($this->isMethod('POST')) { + $rules['password'] = 'required|string|min:8|confirmed'; + } else { + $rules['password'] = 'nullable|string|min:8|confirmed'; + } + + return $rules; + } +} diff --git a/app/Models/User.php b/app/Models/User.php index a6774fa..4115424 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -14,6 +14,8 @@ class User extends Authenticatable implements JWTSubject use HasRoles; protected $table = 'user'; + + protected $guard_name = 'admin'; /** * The attributes that are mass assignable. * @@ -25,7 +27,10 @@ class User extends Authenticatable implements JWTSubject 'lastname', 'username', 'email', - 'email_verified_at' + 'email_verified_at', + 'birthday', + 'address', + 'phone', ]; /** diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 1ff9e8d..3577a19 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -53,6 +53,10 @@ class AppServiceProvider extends ServiceProvider \App\Repositories\Admin\Product\AdminProductRepositoryInterface::class, \App\Repositories\Admin\Product\AdminProductRepository::class ); + $this->app->bind( + \App\Repositories\Employee\EmployeeRepositoryInterface::class, + \App\Repositories\Employee\EmployeeRepository::class + ); if (File::exists(base_path('app/Http/Helpers/function_helper.php'))) { File::requireOnce(base_path('app/Http/Helpers/function_helper.php')); diff --git a/app/Repositories/Employee/EmployeeRepository.php b/app/Repositories/Employee/EmployeeRepository.php new file mode 100644 index 0000000..639f9a8 --- /dev/null +++ b/app/Repositories/Employee/EmployeeRepository.php @@ -0,0 +1,37 @@ +model->with('roles'); + + if (!empty($data['search'])) { + $search = $data['search']; + $query->where(function ($q) use ($search) { + $q->where('firstname', 'like', "%{$search}%") + ->orWhere('lastname', 'like', "%{$search}%") + ->orWhere('username', 'like', "%{$search}%") + ->orWhere('email', 'like', "%{$search}%"); + }); + } + + if (!empty($data['role'])) { + $query->whereHas('roles', fn($q) => $q->where('name', $data['role'])); + } + + $perPage = $data['per_page'] ?? 15; + + return $query->orderBy('id', 'desc')->paginate($perPage); + } +} diff --git a/app/Repositories/Employee/EmployeeRepositoryInterface.php b/app/Repositories/Employee/EmployeeRepositoryInterface.php new file mode 100644 index 0000000..b6c2121 --- /dev/null +++ b/app/Repositories/Employee/EmployeeRepositoryInterface.php @@ -0,0 +1,10 @@ +date('birthday')->nullable()->after('email'); + $table->string('address')->nullable()->after('birthday'); + $table->string('phone', 20)->nullable()->after('address'); + }); + } + + public function down(): void + { + Schema::table('user', function (Blueprint $table) { + $table->dropColumn(['birthday', 'address', 'phone']); + }); + } +}; diff --git a/database/seeders/FixRoleGuardSeeder.php b/database/seeders/FixRoleGuardSeeder.php new file mode 100644 index 0000000..8da4dfb --- /dev/null +++ b/database/seeders/FixRoleGuardSeeder.php @@ -0,0 +1,94 @@ +forgetCachedPermissions(); + + // Cập nhật permissions + DB::table('permissions') + ->where('guard_name', 'admins') + ->update(['guard_name' => 'admin']); + + // Cập nhật roles + DB::table('roles') + ->where('guard_name', 'admins') + ->update(['guard_name' => 'admin']); + + $this->command->info('✅ Đã cập nhật guard_name: admins → admin cho ' . + Role::where('guard_name', 'admin')->count() . ' roles, ' . + Permission::where('guard_name', 'admin')->count() . ' permissions.'); + + // Đảm bảo 6 roles tồn tại với guard admin + $roles = [ + 'Full Administrator', + 'Administrator', + 'Sale', + 'IT', + 'Customer Service', + 'Marketing', + ]; + + foreach ($roles as $name) { + Role::firstOrCreate([ + 'name' => $name, + 'guard_name' => 'admin', + ]); + } + + // Full Administrator luôn có tất cả permissions + $fullAdmin = Role::where('name', 'Full Administrator') + ->where('guard_name', 'admin') + ->first(); + + if ($fullAdmin) { + $fullAdmin->syncPermissions( + Permission::where('guard_name', 'admin')->get() + ); + $this->command->info('✅ Full Administrator đã được cấp tất cả quyền.'); + } + + // Administrator được cấp một số quyền cơ bản + $admin = Role::where('name', 'Administrator') + ->where('guard_name', 'admin') + ->first(); + + if ($admin) { + $adminPermissions = [ + 'customer.view', + 'customer.create', + 'customer.edit', + 'customer.manager', + 'product-service.view', + 'product-service.create', + 'product-service.edit', + 'product-service.manager', + 'order.view', + 'order.create', + 'order.edit', + 'order.manager', + 'administrator.view', + 'administrator.manager', + ]; + $perms = Permission::whereIn('name', $adminPermissions) + ->where('guard_name', 'admin') + ->get(); + $admin->syncPermissions($perms); + $this->command->info('✅ Administrator đã được cấp quyền: ' . implode(', ', $adminPermissions)); + } + + app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions(); + } +} diff --git a/database/seeders/RoleSeeder.php b/database/seeders/RoleSeeder.php index 69b337d..5e78fa7 100644 --- a/database/seeders/RoleSeeder.php +++ b/database/seeders/RoleSeeder.php @@ -2,22 +2,27 @@ namespace Database\Seeders; -use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; use Spatie\Permission\Models\Role; class RoleSeeder extends Seeder { - /** - * Run the database seeds. - * - * @return void - */ - public function run() + public function run(): void { - Role::create([ - 'name' => 'Full Administrator', - 'guard_name' => 'admins' - ]); + $roles = [ + 'Full Administrator', + 'Administrator', + 'Sale', + 'IT', + 'Customer Service', + 'Marketing', + ]; + + foreach ($roles as $name) { + Role::firstOrCreate([ + 'name' => $name, + 'guard_name' => 'admin', + ]); + } } } diff --git a/database/seeders/RolesAndPermissions.php b/database/seeders/RolesAndPermissions.php index bcb7d91..f056ec9 100644 --- a/database/seeders/RolesAndPermissions.php +++ b/database/seeders/RolesAndPermissions.php @@ -2,135 +2,158 @@ namespace Database\Seeders; -use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; use Spatie\Permission\Models\Permission; use Spatie\Permission\Models\Role; - class RolesAndPermissions extends Seeder { - /** - * Run the database seeds. - * - * @return void - */ - public function run() + public function run(): void { // Reset cached roles and permissions app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions(); - // create client permissions - Permission::create(['name' => 'manager customer']); - Permission::create(['name' => 'add customer']); - Permission::create(['name' => 'edit customer']); - Permission::create(['name' => 'delete customer']); + $permissions = [ + // Khách hàng + 'customer.view', + 'customer.create', + 'customer.edit', + 'customer.destroy', + 'customer.manager', - // create admin permissions - Permission::create(['name' => 'manager administrator']); - Permission::create(['name' => 'add administrator']); - Permission::create(['name' => 'edit administrator']); - Permission::create(['name' => 'delete administrator']); + // Nhân sự + 'administrator.view', + 'administrator.create', + 'administrator.edit', + 'administrator.destroy', + 'administrator.manager', - // create product permissions - Permission::create(['name' => 'manager product']); - Permission::create(['name' => 'add product']); - Permission::create(['name' => 'edit product']); - Permission::create(['name' => 'delete product']); + // Sản phẩm / Dịch vụ + 'product-service.view', + 'product-service.create', + 'product-service.edit', + 'product-service.destroy', + 'product-service.manager', - // create order permissions - Permission::create(['name' => 'manager order']); - Permission::create(['name' => 'add order']); - Permission::create(['name' => 'edit order']); - Permission::create(['name' => 'delete order']); + // Đơn hàng + 'order.view', + 'order.create', + 'order.edit', + 'order.destroy', + 'order.manager', - // create invoice permissions - Permission::create(['name' => 'manager invoice']); - Permission::create(['name' => 'add invoice']); - Permission::create(['name' => 'edit invoice']); - Permission::create(['name' => 'delete invoice']); + // Hóa đơn + 'invoice.view', + 'invoice.create', + 'invoice.edit', + 'invoice.destroy', + 'invoice.manager', - // Create Coupon Permissions - Permission::create(['name' => 'manager coupon']); - Permission::create(['name' => 'add coupon']); - Permission::create(['name' => 'edit coupon']); - Permission::create(['name' => 'delete coupon']); + // Mã giảm giá + 'coupon.view', + 'coupon.create', + 'coupon.edit', + 'coupon.destroy', + 'coupon.manager', - // Create post Permissions - Permission::create(['name' => 'manager post']); - Permission::create(['name' => 'add post']); - Permission::create(['name' => 'edit post']); - Permission::create(['name' => 'delete post']); + // Bài viết + 'post.view', + 'post.create', + 'post.edit', + 'post.destroy', + 'post.manager', - // Create support Permissions - Permission::create(['name' => 'manager support']); - Permission::create(['name' => 'add support']); - Permission::create(['name' => 'edit support']); - Permission::create(['name' => 'delete support']); + // Hỗ trợ + 'support.view', + 'support.create', + 'support.edit', + 'support.destroy', + 'support.manager', - // Create Payment Permissions - Permission::create(['name' => 'manager payment']); - Permission::create(['name' => 'add payment']); - Permission::create(['name' => 'edit payment']); - Permission::create(['name' => 'delete payment']); + // Thanh toán + 'payment.view', + 'payment.create', + 'payment.edit', + 'payment.destroy', + 'payment.manager', - // create module dns permissions - Permission::create(['name' => 'manager module dns']); - Permission::create(['name' => 'add module dns']); - Permission::create(['name' => 'edit module dns']); - Permission::create(['name' => 'delete module dns']); + // Module DNS + 'module-dns.view', + 'module-dns.create', + 'module-dns.edit', + 'module-dns.destroy', + 'module-dns.manager', - // create module hosting permissions - Permission::create(['name' => 'manager module hosting']); - Permission::create(['name' => 'add module hosting']); - Permission::create(['name' => 'edit module hosting']); - Permission::create(['name' => 'delete module hosting']); + // Module Hosting + 'module-hosting.view', + 'module-hosting.create', + 'module-hosting.edit', + 'module-hosting.destroy', + 'module-hosting.manager', - // create module vcenter permissions - Permission::create(['name' => 'manager module vcenter']); - Permission::create(['name' => 'add module vcenter']); - Permission::create(['name' => 'edit module vcenter']); - Permission::create(['name' => 'delete module vcenter']); + // Module vCenter + 'module-vcenter.view', + 'module-vcenter.create', + 'module-vcenter.edit', + 'module-vcenter.destroy', + 'module-vcenter.manager', - // create module ippool permissions - Permission::create(['name' => 'manager module ippool']); - Permission::create(['name' => 'add module ippool']); - Permission::create(['name' => 'edit module ippool']); - Permission::create(['name' => 'delete module ippool']); + // IP Pool + 'module-ippool.view', + 'module-ippool.create', + 'module-ippool.edit', + 'module-ippool.destroy', + 'module-ippool.manager', - // create package permissions - Permission::create(['name' => 'manager package module']); - Permission::create(['name' => 'add package module']); - Permission::create(['name' => 'edit package module']); - Permission::create(['name' => 'delete package module']); + // Gói dịch vụ + 'package-module.view', + 'package-module.create', + 'package-module.edit', + 'package-module.destroy', + 'package-module.manager', - // create email template permissions - Permission::create(['name' => 'manager email template']); - Permission::create(['name' => 'add email template']); - Permission::create(['name' => 'edit email template']); - Permission::create(['name' => 'delete email template']); + // Email Template + 'email-template.view', + 'email-template.create', + 'email-template.edit', + 'email-template.destroy', + 'email-template.manager', - // create setting permissions - Permission::create(['name' => 'manager setting']); - Permission::create(['name' => 'manager smtp']); - Permission::create(['name' => 'manager auto banking']); - Permission::create(['name' => 'manager system log']); + // Cài đặt + 'setting.manager', + 'smtp.manager', + 'auto-banking.manager', + 'system-log.manager', - // create role permissions - Permission::create(['name' => 'manager role']); - Permission::create(['name' => 'add role']); - Permission::create(['name' => 'edit role']); - Permission::create(['name' => 'delete role']); + // Vai trò & Phân quyền + 'role.view', + 'role.create', + 'role.edit', + 'role.destroy', + 'role.manager', + // Menu + 'menu.view', + 'menu.create', + 'menu.edit', + 'menu.destroy', + 'menu.manager', + ]; - // Create Menu Permissions - Permission::create(['name' => 'manager menu']); - Permission::create(['name' => 'add menu']); - Permission::create(['name' => 'edit menu']); - Permission::create(['name' => 'delete menu']); - // this can be done as separate statements - $role = Role::find(1); + foreach ($permissions as $name) { + Permission::firstOrCreate([ + 'name' => $name, + 'guard_name' => 'admin', + ]); + } - $role->givePermissionTo(Permission::all()); + // Full Administrator nhận tất cả quyền + $fullAdmin = Role::where('name', 'Full Administrator') + ->where('guard_name', 'admin') + ->first(); + + if ($fullAdmin) { + $fullAdmin->syncPermissions(Permission::where('guard_name', 'admin')->get()); + } } } diff --git a/routes/api.php b/routes/api.php index f2358d8..33ac06d 100644 --- a/routes/api.php +++ b/routes/api.php @@ -2,6 +2,8 @@ use App\Http\Controllers\Api\Admin\Auth\AuthController; use App\Http\Controllers\Api\Admin\Customer\CustomerController; +use App\Http\Controllers\Api\Admin\Employee\EmployeeController; +use App\Http\Controllers\Api\Admin\Role\RolePermissionController; use App\Http\Controllers\Api\Admin\Invoice\InvoiceController; use App\Http\Controllers\Api\Admin\Order\OrderController; use App\Http\Controllers\Api\Admin\Payment\PaymentController; @@ -38,105 +40,128 @@ Route::prefix('v1') Route::post('refresh', [AuthController::class, 'refresh']); Route::get('me', [AuthController::class, 'me']); - // Customers + // Role Permissions + Route::prefix('role-permissions')->group(function () { + // Accessible bởi mọi admin — trả về permissions hiệu lực của user hiện tại + Route::get('mine', [RolePermissionController::class, 'mine']); + // Các endpoint sau chỉ dành cho Full Administrator (kiểm tra trong controller) + Route::get('/', [RolePermissionController::class, 'index']); + Route::get('all-permissions', [RolePermissionController::class, 'allPermissions']); + Route::post('permissions', [RolePermissionController::class, 'createPermissionGroup']); + Route::delete('permissions/group/{module}', [RolePermissionController::class, 'deletePermissionGroup']); + Route::patch('permissions/{id}', [RolePermissionController::class, 'renamePermission']); + Route::put('{roleId}', [RolePermissionController::class, 'update']); + }); + + // Employees (module: administrator) + Route::prefix('employees')->group(function () { + Route::get('roles', [EmployeeController::class, 'roles'])->middleware('perm:administrator.view'); + Route::get('/', [EmployeeController::class, 'index'])->middleware('perm:administrator.view'); + Route::post('/', [EmployeeController::class, 'store'])->middleware('perm:administrator.create'); + Route::get('{id}', [EmployeeController::class, 'show'])->middleware('perm:administrator.view'); + Route::put('{id}', [EmployeeController::class, 'update'])->middleware('perm:administrator.edit'); + Route::delete('{id}', [EmployeeController::class, 'destroy'])->middleware('perm:administrator.destroy'); + }); + + // Customers (module: customer) Route::prefix('customers')->group(function () { - Route::get('/', [CustomerController::class, 'index']); - Route::get('/datatable', [CustomerController::class, 'datatable']); - Route::post('/', [CustomerController::class, 'store']); - Route::get('{id}', [CustomerController::class, 'show']); - Route::put('{id}', [CustomerController::class, 'update']); - Route::delete('{id}', [CustomerController::class, 'destroy']); - Route::post('{id}/reset-password', [CustomerController::class, 'resetPassword']); - Route::post('{id}/add-credit', [CustomerController::class, 'addCredit']); - Route::get('{id}/products', [CustomerController::class, 'getProducts']); - Route::get('{id}/invoices', [CustomerController::class, 'getInvoices']); - Route::get('{id}/transactions', [CustomerController::class, 'getTransactions']); - Route::get('{id}/domains', [CustomerController::class, 'getDomains']); - Route::get('trash', [CustomerController::class, 'trash']); - Route::post('{id}/restore', [CustomerController::class, 'restore']); - Route::delete('{id}/force', [CustomerController::class, 'forceDelete']); + Route::get('trash', [CustomerController::class, 'trash'])->middleware('perm:customer.view'); + Route::get('/', [CustomerController::class, 'index'])->middleware('perm:customer.view'); + Route::get('/datatable', [CustomerController::class, 'datatable'])->middleware('perm:customer.view'); + Route::post('/', [CustomerController::class, 'store'])->middleware('perm:customer.create'); + Route::get('{id}', [CustomerController::class, 'show'])->middleware('perm:customer.view'); + Route::put('{id}', [CustomerController::class, 'update'])->middleware('perm:customer.edit'); + Route::delete('{id}', [CustomerController::class, 'destroy'])->middleware('perm:customer.destroy'); + Route::post('{id}/reset-password', [CustomerController::class, 'resetPassword'])->middleware('perm:customer.edit'); + Route::post('{id}/add-credit', [CustomerController::class, 'addCredit'])->middleware('perm:customer.edit'); + Route::get('{id}/products', [CustomerController::class, 'getProducts'])->middleware('perm:customer.view'); + Route::get('{id}/invoices', [CustomerController::class, 'getInvoices'])->middleware('perm:customer.view'); + Route::get('{id}/transactions', [CustomerController::class, 'getTransactions'])->middleware('perm:customer.view'); + Route::get('{id}/domains', [CustomerController::class, 'getDomains'])->middleware('perm:customer.view'); + Route::post('{id}/restore', [CustomerController::class, 'restore'])->middleware('perm:customer.edit'); + Route::delete('{id}/force', [CustomerController::class, 'forceDelete'])->middleware('perm:customer.destroy'); }); - // Products + // Products (module: product-service) Route::prefix('products')->group(function () { - Route::get('/', [ProductController::class, 'index']); - Route::post('/', [ProductController::class, 'store']); - Route::get('{id}', [ProductController::class, 'show']); - Route::put('{id}', [ProductController::class, 'update']); - Route::delete('{id}', [ProductController::class, 'destroy']); - Route::post('{id}/duplicate', [ProductController::class, 'duplicate']); - Route::post('import', [ProductController::class, 'import']); - Route::get('export', [ProductController::class, 'export']); + Route::get('export', [ProductController::class, 'export'])->middleware('perm:product-service.manager'); + Route::get('/', [ProductController::class, 'index'])->middleware('perm:product-service.view'); + Route::post('/', [ProductController::class, 'store'])->middleware('perm:product-service.create'); + Route::post('import', [ProductController::class, 'import'])->middleware('perm:product-service.create'); + Route::get('{id}', [ProductController::class, 'show'])->middleware('perm:product-service.view'); + Route::put('{id}', [ProductController::class, 'update'])->middleware('perm:product-service.edit'); + Route::delete('{id}', [ProductController::class, 'destroy'])->middleware('perm:product-service.destroy'); + Route::post('{id}/duplicate', [ProductController::class, 'duplicate'])->middleware('perm:product-service.create'); }); - // Product Categories (Groups) + // Product Categories / Groups (module: product-service) Route::prefix('product-categories')->group(function () { - Route::get('/', [ProductCategoryController::class, 'index']); - Route::post('/', [ProductCategoryController::class, 'store']); - Route::get('{id}', [ProductCategoryController::class, 'show']); - Route::put('{id}', [ProductCategoryController::class, 'update']); - Route::delete('{id}', [ProductCategoryController::class, 'destroy']); + Route::get('/', [ProductCategoryController::class, 'index'])->middleware('perm:product-service.view'); + Route::post('/', [ProductCategoryController::class, 'store'])->middleware('perm:product-service.create'); + Route::get('{id}', [ProductCategoryController::class, 'show'])->middleware('perm:product-service.view'); + Route::put('{id}', [ProductCategoryController::class, 'update'])->middleware('perm:product-service.edit'); + Route::delete('{id}', [ProductCategoryController::class, 'destroy'])->middleware('perm:product-service.destroy'); }); - // Orders + // Orders (module: order) Route::prefix('orders')->group(function () { - Route::get('/', [OrderController::class, 'index']); - Route::get('/datatable', [OrderController::class, 'datatable']); - Route::post('/', [OrderController::class, 'store']); - Route::get('{id}', [OrderController::class, 'show']); - Route::put('{id}', [OrderController::class, 'update']); - Route::delete('{id}', [OrderController::class, 'destroy']); - Route::post('{id}/accept', [OrderController::class, 'accept']); - Route::post('{id}/cancel', [OrderController::class, 'cancel']); + Route::get('/', [OrderController::class, 'index'])->middleware('perm:order.view'); + Route::get('/datatable', [OrderController::class, 'datatable'])->middleware('perm:order.view'); + Route::post('/', [OrderController::class, 'store'])->middleware('perm:order.create'); + Route::get('{id}', [OrderController::class, 'show'])->middleware('perm:order.view'); + Route::put('{id}', [OrderController::class, 'update'])->middleware('perm:order.edit'); + Route::delete('{id}', [OrderController::class, 'destroy'])->middleware('perm:order.destroy'); + Route::post('{id}/accept', [OrderController::class, 'accept'])->middleware('perm:order.edit'); + Route::post('{id}/cancel', [OrderController::class, 'cancel'])->middleware('perm:order.edit'); }); - // Invoices + // Invoices (module: invoice) Route::prefix('invoices')->group(function () { - Route::get('/', [InvoiceController::class, 'index']); - Route::get('/datatable', [InvoiceController::class, 'datatable']); - Route::post('/', [InvoiceController::class, 'store']); - Route::get('{id}', [InvoiceController::class, 'show']); - Route::put('{id}', [InvoiceController::class, 'update']); - Route::delete('{id}', [InvoiceController::class, 'destroy']); - Route::post('{id}/paid', [InvoiceController::class, 'markAsPaid']); - Route::post('{id}/cancel', [InvoiceController::class, 'cancel']); + Route::get('/', [InvoiceController::class, 'index'])->middleware('perm:invoice.view'); + Route::get('/datatable', [InvoiceController::class, 'datatable'])->middleware('perm:invoice.view'); + Route::post('/', [InvoiceController::class, 'store'])->middleware('perm:invoice.create'); + Route::get('{id}', [InvoiceController::class, 'show'])->middleware('perm:invoice.view'); + Route::put('{id}', [InvoiceController::class, 'update'])->middleware('perm:invoice.edit'); + Route::delete('{id}', [InvoiceController::class, 'destroy'])->middleware('perm:invoice.destroy'); + Route::post('{id}/paid', [InvoiceController::class, 'markAsPaid'])->middleware('perm:invoice.edit'); + Route::post('{id}/cancel', [InvoiceController::class, 'cancel'])->middleware('perm:invoice.edit'); }); - // Payments + // Payments (module: payment) Route::prefix('payments')->group(function () { - Route::get('/', [PaymentController::class, 'index']); - Route::post('/', [PaymentController::class, 'store']); - Route::get('{id}', [PaymentController::class, 'show']); - Route::put('{id}', [PaymentController::class, 'update']); - Route::delete('{id}', [PaymentController::class, 'destroy']); + Route::get('/', [PaymentController::class, 'index'])->middleware('perm:payment.view'); + Route::post('/', [PaymentController::class, 'store'])->middleware('perm:payment.create'); + Route::get('{id}', [PaymentController::class, 'show'])->middleware('perm:payment.view'); + Route::put('{id}', [PaymentController::class, 'update'])->middleware('perm:payment.edit'); + Route::delete('{id}', [PaymentController::class, 'destroy'])->middleware('perm:payment.destroy'); }); - // VPS + // VPS (module: module-vcenter) Route::prefix('vps')->group(function () { - Route::get('/', [VPSController::class, 'index']); - Route::get('{id}', [VPSController::class, 'show']); - Route::put('{id}', [VPSController::class, 'update']); - Route::post('{id}/power-on', [VPSController::class, 'powerOn']); - Route::post('{id}/power-off', [VPSController::class, 'powerOff']); - Route::post('{id}/restart', [VPSController::class, 'restart']); - Route::get('{id}/console-ticket', [VPSController::class, 'getConsoleTicket']); + Route::get('/', [VPSController::class, 'index'])->middleware('perm:module-vcenter.view'); + Route::get('{id}', [VPSController::class, 'show'])->middleware('perm:module-vcenter.view'); + Route::put('{id}', [VPSController::class, 'update'])->middleware('perm:module-vcenter.edit'); + Route::post('{id}/power-on', [VPSController::class, 'powerOn'])->middleware('perm:module-vcenter.edit'); + Route::post('{id}/power-off', [VPSController::class, 'powerOff'])->middleware('perm:module-vcenter.edit'); + Route::post('{id}/restart', [VPSController::class, 'restart'])->middleware('perm:module-vcenter.edit'); + Route::get('{id}/console-ticket', [VPSController::class, 'getConsoleTicket'])->middleware('perm:module-vcenter.view'); }); - // vCenter + // vCenter (module: module-vcenter) Route::prefix('vcenter')->group(function () { - Route::get('tree', [VCenterController::class, 'tree']); - Route::get('console-ticket', [VCenterController::class, 'consoleTicket']); - Route::post('/', [VCenterController::class, 'store']); - Route::post('sync', [VCenterController::class, 'sync']); - Route::put('node/{type}/{id}', [VCenterController::class, 'updateNode']); - Route::delete('node/{type}/{id}', [VCenterController::class, 'destroyNode']); + Route::get('tree', [VCenterController::class, 'tree'])->middleware('perm:module-vcenter.view'); + Route::get('console-ticket', [VCenterController::class, 'consoleTicket'])->middleware('perm:module-vcenter.view'); + Route::post('/', [VCenterController::class, 'store'])->middleware('perm:module-vcenter.create'); + Route::post('sync', [VCenterController::class, 'sync'])->middleware('perm:module-vcenter.edit'); + Route::put('node/{type}/{id}', [VCenterController::class, 'updateNode'])->middleware('perm:module-vcenter.edit'); + Route::delete('node/{type}/{id}', [VCenterController::class, 'destroyNode'])->middleware('perm:module-vcenter.destroy'); }); - // vCenter Manager Power Operations + // vCenter Manager Power Operations (module: module-vcenter) Route::prefix('vcenter-manager')->group(function () { - Route::post('vm/power-on', [App\Http\Controllers\admin\VCenterManagerController::class, 'powerOn']); - Route::post('vm/power-off', [App\Http\Controllers\admin\VCenterManagerController::class, 'powerOff']); - Route::get('vms/powered-off', [App\Http\Controllers\admin\VCenterManagerController::class, 'getPoweredOffVms']); + Route::post('vm/power-on', [App\Http\Controllers\admin\VCenterManagerController::class, 'powerOn'])->middleware('perm:module-vcenter.edit'); + Route::post('vm/power-off', [App\Http\Controllers\admin\VCenterManagerController::class, 'powerOff'])->middleware('perm:module-vcenter.edit'); + Route::get('vms/powered-off', [App\Http\Controllers\admin\VCenterManagerController::class, 'getPoweredOffVms'])->middleware('perm:module-vcenter.view'); }); }); });