From b39e8b5f2c4e57433e82499ca7ea6221e9b67f90 Mon Sep 17 00:00:00 2001 From: kent Date: Mon, 1 Dec 2025 00:13:12 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20[users]=20=EC=8A=88=ED=8D=BC=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=EC=9E=90=20=EB=B3=B4=ED=98=B8=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EB=B3=B5=EC=9B=90=20=EB=9D=BC=EC=9A=B0=ED=8A=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - routes/api.php: 8개 엔티티의 restore 라우트를 super.admin 미들웨어 밖으로 이동 - tenants, departments, users, menus, boards - pm/projects, pm/tasks, pm/issues - UserService.canAccessUser(): withTrashed() 적용하여 soft-deleted 사용자 권한 체크 가능 - UserPermissionService.canModifyUser(): withTrashed() 적용 (일관성 유지) 권한 정책: - 복원 (Restore): 일반관리자 가능 - 영구삭제 (Force Delete): 슈퍼관리자 전용 버그 수정: - 302 Found 에러 해결 (미들웨어 블로킹) - soft-deleted 사용자 복원 시 권한 체크 실패 해결 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../Controllers/Api/Admin/UserController.php | 24 +++ .../Api/Admin/UserPermissionController.php | 40 ++++ app/Http/Controllers/UserController.php | 10 +- app/Services/UserPermissionService.php | 36 +++- app/Services/UserService.php | 51 ++++- claudedocs/super-admin-protection.md | 174 ++++++++++++++++++ .../views/boards/partials/table.blade.php | 6 +- .../departments/partials/table.blade.php | 6 +- .../views/menus/partials/table.blade.php | 6 +- .../projects/partials/table.blade.php | 8 +- .../views/tenants/partials/table.blade.php | 6 +- .../views/users/partials/modal-info.blade.php | 5 +- .../views/users/partials/table.blade.php | 9 +- routes/api.php | 48 +++-- 14 files changed, 376 insertions(+), 53 deletions(-) create mode 100644 claudedocs/super-admin-protection.md diff --git a/app/Http/Controllers/Api/Admin/UserController.php b/app/Http/Controllers/Api/Admin/UserController.php index bd8cc0f9..87394813 100644 --- a/app/Http/Controllers/Api/Admin/UserController.php +++ b/app/Http/Controllers/Api/Admin/UserController.php @@ -50,6 +50,14 @@ public function index(Request $request): JsonResponse */ public function show(int $id): JsonResponse { + // 슈퍼관리자 보호: 일반관리자가 슈퍼관리자 정보 조회 불가 + if (! $this->userService->canAccessUser($id)) { + return response()->json([ + 'success' => false, + 'message' => '사용자를 찾을 수 없습니다.', + ], 404); + } + $user = $this->userService->getUserById($id); if (! $user) { @@ -165,6 +173,14 @@ public function destroy(int $id): JsonResponse */ public function restore(Request $request, int $id): JsonResponse { + // 슈퍼관리자 보호: 일반관리자가 슈퍼관리자 복원 불가 (존재하지 않는 것처럼) + if (! $this->userService->canAccessUser($id)) { + return response()->json([ + 'success' => false, + 'message' => '사용자를 찾을 수 없습니다.', + ], 404); + } + $this->userService->restoreUser($id); // HTMX 요청 시 테이블 새로고침 트리거 @@ -187,6 +203,14 @@ public function restore(Request $request, int $id): JsonResponse */ public function modal(Request $request, int $id): JsonResponse { + // 슈퍼관리자 보호: 일반관리자가 슈퍼관리자 정보 조회 불가 + if (! $this->userService->canAccessUser($id)) { + return response()->json([ + 'success' => false, + 'message' => '사용자를 찾을 수 없습니다.', + ], 404); + } + $user = $this->userService->getUserForModal($id); if (! $user) { diff --git a/app/Http/Controllers/Api/Admin/UserPermissionController.php b/app/Http/Controllers/Api/Admin/UserPermissionController.php index 3a8966b0..203e10fa 100644 --- a/app/Http/Controllers/Api/Admin/UserPermissionController.php +++ b/app/Http/Controllers/Api/Admin/UserPermissionController.php @@ -62,6 +62,14 @@ public function getMatrix(Request $request) return view('user-permissions.partials.empty-state'); } + // 슈퍼관리자 보호: 일반관리자가 슈퍼관리자 권한 조회 불가 + if (! $this->userPermissionService->canModifyUser($userId)) { + return response()->json([ + 'success' => false, + 'message' => '슈퍼관리자의 권한은 조회할 수 없습니다.', + ], 403); + } + // 사용자의 tenant_id로 메뉴 필터링 $tenantId = $this->getEffectiveTenantId($request, $userId); @@ -89,6 +97,14 @@ public function toggle(Request $request) $guardName = $this->getValidatedGuardName($request); $tenantId = $this->getEffectiveTenantId($request, $userId); + // 슈퍼관리자 보호: 일반관리자가 슈퍼관리자 권한 수정 불가 + if (! $this->userPermissionService->canModifyUser($userId)) { + return response()->json([ + 'success' => false, + 'message' => '슈퍼관리자의 권한은 수정할 수 없습니다.', + ], 403); + } + $newValue = $this->userPermissionService->togglePermission( $userId, $menuId, @@ -117,6 +133,14 @@ public function allowAll(Request $request) $guardName = $this->getValidatedGuardName($request); $tenantId = $this->getEffectiveTenantId($request, $userId); + // 슈퍼관리자 보호: 일반관리자가 슈퍼관리자 권한 수정 불가 + if (! $this->userPermissionService->canModifyUser($userId)) { + return response()->json([ + 'success' => false, + 'message' => '슈퍼관리자의 권한은 수정할 수 없습니다.', + ], 403); + } + $this->userPermissionService->allowAllPermissions($userId, $tenantId, $guardName); // 전체 매트릭스 다시 로드 @@ -139,6 +163,14 @@ public function denyAll(Request $request) $guardName = $this->getValidatedGuardName($request); $tenantId = $this->getEffectiveTenantId($request, $userId); + // 슈퍼관리자 보호: 일반관리자가 슈퍼관리자 권한 수정 불가 + if (! $this->userPermissionService->canModifyUser($userId)) { + return response()->json([ + 'success' => false, + 'message' => '슈퍼관리자의 권한은 수정할 수 없습니다.', + ], 403); + } + $this->userPermissionService->denyAllPermissions($userId, $tenantId, $guardName); // 전체 매트릭스 다시 로드 @@ -161,6 +193,14 @@ public function reset(Request $request) $guardName = $this->getValidatedGuardName($request); $tenantId = $this->getEffectiveTenantId($request, $userId); + // 슈퍼관리자 보호: 일반관리자가 슈퍼관리자 권한 수정 불가 + if (! $this->userPermissionService->canModifyUser($userId)) { + return response()->json([ + 'success' => false, + 'message' => '슈퍼관리자의 권한은 수정할 수 없습니다.', + ], 403); + } + $this->userPermissionService->resetToDefaultPermissions($userId, $tenantId, $guardName); // 전체 매트릭스 다시 로드 diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index a681c635..c0312f4f 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -41,17 +41,17 @@ public function create(): View */ public function edit(int $id): View { + // 슈퍼관리자 보호: 일반관리자가 슈퍼관리자에 접근 불가 (존재하지 않는 것처럼) + if (! $this->userService->canAccessUser($id)) { + abort(404, '사용자를 찾을 수 없습니다.'); + } + $user = $this->userService->getUserById($id); if (! $user) { abort(404, '사용자를 찾을 수 없습니다.'); } - // 슈퍼관리자 보호: 일반관리자가 슈퍼관리자를 수정하려는 경우 차단 - if ($user->is_super_admin && ! auth()->user()?->is_super_admin) { - abort(403, '슈퍼관리자는 수정할 수 없습니다.'); - } - $tenantId = session('selected_tenant_id'); // 역할/부서 목록 (테넌트별) diff --git a/app/Services/UserPermissionService.php b/app/Services/UserPermissionService.php index ccc86331..87f3195d 100644 --- a/app/Services/UserPermissionService.php +++ b/app/Services/UserPermissionService.php @@ -691,21 +691,49 @@ public function hasPermission(int $userId, int $menuId, string $permissionType, ->exists(); } + /** + * 대상 사용자가 슈퍼관리자인지 검증 (일반관리자는 슈퍼관리자 수정 불가) + * + * @param int $targetUserId 대상 사용자 ID + * @return bool true면 수정 가능, false면 수정 불가 + */ + public function canModifyUser(int $targetUserId): bool + { + // withTrashed()를 사용하여 일관성 유지 + $targetUser = User::withTrashed()->find($targetUserId); + $currentUser = auth()->user(); + + // 대상 사용자가 슈퍼관리자이고 현재 사용자가 슈퍼관리자가 아니면 수정 불가 + if ($targetUser?->is_super_admin && ! $currentUser?->is_super_admin) { + return false; + } + + return true; + } + /** * 테넌트별 사용자 목록 조회 (권한 개수 포함) + * 일반관리자는 슈퍼관리자가 목록에서 제외됨 * * @param int $tenantId 테넌트 ID * @return \Illuminate\Support\Collection 사용자 목록 */ public function getUsersByTenant(int $tenantId): \Illuminate\Support\Collection { - $users = User::whereHas('tenants', function ($query) use ($tenantId) { + $currentUser = auth()->user(); + + $query = User::whereHas('tenants', function ($query) use ($tenantId) { $query->where('tenants.id', $tenantId) ->where('user_tenants.is_active', true); }) - ->where('is_active', true) - ->orderBy('name') - ->get(); + ->where('is_active', true); + + // 일반관리자는 슈퍼관리자를 볼 수 없음 + if (! $currentUser?->is_super_admin) { + $query->where('is_super_admin', false); + } + + $users = $query->orderBy('name')->get(); // 각 사용자별 권한 개수 계산 $now = now(); diff --git a/app/Services/UserService.php b/app/Services/UserService.php index 0f37f955..227a5c7e 100644 --- a/app/Services/UserService.php +++ b/app/Services/UserService.php @@ -6,10 +6,15 @@ use App\Models\User; use App\Models\UserRole; use Illuminate\Contracts\Pagination\LengthAwarePaginator; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; class UserService { + public function __construct( + private readonly ArchiveService $archiveService + ) {} + /** * 사용자 목록 조회 (페이지네이션) */ @@ -18,6 +23,11 @@ public function getUsers(array $filters = [], int $perPage = 15): LengthAwarePag $tenantId = session('selected_tenant_id'); $query = User::query()->withTrashed(); + // 슈퍼관리자 보호: 일반관리자는 슈퍼관리자를 볼 수 없음 + if (! auth()->user()?->is_super_admin) { + $query->where('is_super_admin', false); + } + // 역할/부서 관계 eager loading (테넌트별) if ($tenantId) { $query->with([ @@ -223,15 +233,25 @@ public function restoreUser(int $id): bool /** * 사용자 영구 삭제 (슈퍼관리자 전용) + * + * 1. 사용자 데이터를 아카이브에 저장 + * 2. 관련 데이터 삭제 + * 3. 사용자 영구 삭제 */ public function forceDeleteUser(int $id): bool { $user = User::withTrashed()->findOrFail($id); - // 관련 데이터 먼저 삭제 - $user->tenants()->detach(); // user_tenants 관계 삭제 + return DB::transaction(function () use ($user) { + // 1. 아카이브에 저장 (복원 가능하도록) + $this->archiveService->archiveUser($user); - return $user->forceDelete(); + // 2. 관련 데이터 삭제 + $user->tenants()->detach(); // user_tenants 관계 삭제 + + // 3. 사용자 영구 삭제 + return $user->forceDelete(); + }); } /** @@ -368,6 +388,11 @@ public function getActiveUsers() $tenantId = session('selected_tenant_id'); $query = User::query()->where('is_active', true); + // 슈퍼관리자 보호: 일반관리자는 슈퍼관리자를 볼 수 없음 + if (! auth()->user()?->is_super_admin) { + $query->where('is_super_admin', false); + } + // 테넌트 필터링 (user_tenants pivot을 통한 필터링) if ($tenantId) { $query->whereHas('tenants', function ($q) use ($tenantId) { @@ -377,4 +402,24 @@ public function getActiveUsers() return $query->orderBy('name')->get(); } + + /** + * 슈퍼관리자 보호: 일반관리자가 슈퍼관리자에 접근할 수 있는지 확인 + * + * @param int $targetUserId 대상 사용자 ID + * @return bool true면 접근 가능, false면 접근 불가 + */ + public function canAccessUser(int $targetUserId): bool + { + // withTrashed()를 사용하여 soft-deleted 사용자도 확인 (복원 시 필요) + $targetUser = User::withTrashed()->find($targetUserId); + $currentUser = auth()->user(); + + // 대상 사용자가 슈퍼관리자이고 현재 사용자가 슈퍼관리자가 아니면 접근 불가 + if ($targetUser?->is_super_admin && ! $currentUser?->is_super_admin) { + return false; + } + + return true; + } } diff --git a/claudedocs/super-admin-protection.md b/claudedocs/super-admin-protection.md new file mode 100644 index 00000000..5881fbab --- /dev/null +++ b/claudedocs/super-admin-protection.md @@ -0,0 +1,174 @@ +# Super Admin Protection Feature + +**날짜:** 2025-12-01 +**작업자:** Claude Code +**요청:** 슈퍼관리자 보호 및 복원/영구삭제 권한 분리 + +--- + +## 1. 요구사항 + +### 1.1 슈퍼관리자 보호 +- 일반관리자는 슈퍼관리자를 **볼 수 없음** (목록에서 숨김) +- 일반관리자는 슈퍼관리자를 **수정/삭제할 수 없음** +- 슈퍼관리자만 다른 슈퍼관리자를 관리 가능 + +### 1.2 복원/영구삭제 권한 분리 +- **복원 (Restore)**: 일반관리자도 가능 +- **영구삭제 (Force Delete)**: 슈퍼관리자 전용 + +--- + +## 2. 구현 내용 + +### 2.1 라우트 수정 (`routes/api.php`) + +8개 엔티티의 restore 라우트를 `super.admin` 미들웨어 밖으로 이동: + +| 엔티티 | 라인 | 복원 라우트 | 영구삭제 라우트 | +|--------|------|-------------|-----------------| +| Tenants | 42-48 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) | +| Departments | 76-82 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) | +| Users | 93-99 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) | +| Menus | 117-123 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) | +| Boards | 151-157 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) | +| PM Projects | 234-240 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) | +| PM Tasks | 260-266 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) | +| PM Issues | 292-298 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) | + +**패턴:** +```php +// 복원 (일반관리자 가능) +Route::post('/{id}/restore', [Controller::class, 'restore'])->name('restore'); + +// 슈퍼관리자 전용 액션 (영구삭제) +Route::middleware('super.admin')->group(function () { + Route::delete('/{id}/force', [Controller::class, 'forceDestroy'])->name('forceDestroy'); +}); +``` + +### 2.2 서비스 레이어 수정 + +#### `app/Services/UserService.php` +```php +public function canAccessUser(int $targetUserId): bool +{ + // withTrashed()를 사용하여 soft-deleted 사용자도 확인 (복원 시 필요) + $targetUser = User::withTrashed()->find($targetUserId); + $currentUser = auth()->user(); + + // 대상 사용자가 슈퍼관리자이고 현재 사용자가 슈퍼관리자가 아니면 접근 불가 + if ($targetUser?->is_super_admin && ! $currentUser?->is_super_admin) { + return false; + } + + return true; +} +``` + +#### `app/Services/UserPermissionService.php` +```php +public function canModifyUser(int $targetUserId): bool +{ + // withTrashed()를 사용하여 일관성 유지 + $targetUser = User::withTrashed()->find($targetUserId); + $currentUser = auth()->user(); + + if ($targetUser?->is_super_admin && ! $currentUser?->is_super_admin) { + return false; + } + + return true; +} +``` + +**핵심 수정**: `User::find()` → `User::withTrashed()->find()` +- Soft-deleted 사용자도 조회 가능하게 변경 +- 복원 작업 시 권한 체크가 정상 작동 + +### 2.3 뷰 레이어 수정 + +6개 테이블 뷰에 권한별 버튼 표시 로직 적용: + +| 파일 | 복원 버튼 | 영구삭제 버튼 | +|------|-----------|---------------| +| `users/partials/table.blade.php` | `$canModify` 체크 | `is_super_admin` 체크 | +| `users/partials/modal-info.blade.php` | 슈퍼관리자이거나 대상이 일반사용자 | - | +| `departments/partials/table.blade.php` | 항상 표시 | `is_super_admin` 체크 | +| `menus/partials/table.blade.php` | 항상 표시 | `is_super_admin` 체크 | +| `boards/partials/table.blade.php` | 항상 표시 | `is_super_admin` 체크 | +| `tenants/partials/table.blade.php` | 항상 표시 | `is_super_admin` 체크 | +| `project-management/projects/partials/table.blade.php` | 항상 표시 | `is_super_admin` 체크 | + +**Blade 패턴:** +```blade +@if($item->deleted_at) + + + @if(auth()->user()?->is_super_admin) + + @endif +@endif +``` + +--- + +## 3. 수정된 파일 목록 + +### 라우트 +- `routes/api.php` - 8개 엔티티 restore 라우트 분리 + +### 서비스 +- `app/Services/UserService.php` - `canAccessUser()` withTrashed 적용 +- `app/Services/UserPermissionService.php` - `canModifyUser()` withTrashed 적용 + +### 뷰 (Blade) +- `resources/views/users/partials/table.blade.php` +- `resources/views/users/partials/modal-info.blade.php` +- `resources/views/departments/partials/table.blade.php` +- `resources/views/menus/partials/table.blade.php` +- `resources/views/boards/partials/table.blade.php` +- `resources/views/tenants/partials/table.blade.php` +- `resources/views/project-management/projects/partials/table.blade.php` + +--- + +## 4. 테스트 시나리오 + +### 4.1 일반관리자 테스트 +- [ ] 사용자 목록에서 슈퍼관리자가 보이지 않음 +- [ ] 삭제된 사용자 복원 가능 +- [ ] 삭제된 부서/메뉴/게시판/테넌트/프로젝트 복원 가능 +- [ ] 영구삭제 버튼이 보이지 않음 +- [ ] 슈퍼관리자 수정/삭제 불가 (API 레벨) + +### 4.2 슈퍼관리자 테스트 +- [ ] 모든 사용자 조회 가능 (슈퍼관리자 포함) +- [ ] 삭제된 항목 복원 가능 +- [ ] 영구삭제 가능 +- [ ] 다른 슈퍼관리자 관리 가능 + +--- + +## 5. 이슈 해결 + +### 5.1 302 Found 에러 +**문제**: 일반관리자가 복원 API 호출 시 302 리다이렉트 발생 +**원인**: restore 라우트가 `super.admin` 미들웨어 내부에 있었음 +**해결**: restore 라우트를 미들웨어 밖으로 이동 + +### 5.2 Soft-deleted 사용자 권한 체크 실패 +**문제**: `User::find()`가 soft-deleted 사용자를 조회하지 못함 +**원인**: Eloquent 기본 동작으로 soft-deleted 레코드 제외 +**해결**: `User::withTrashed()->find()` 사용 + +--- + +## 6. 관련 문서 + +- `claudedocs/archive-restore-feature-analysis.md` - 아카이브/복원 기능 분석 +- `CURRENT_WORKS.md` - 작업 히스토리 \ No newline at end of file diff --git a/resources/views/boards/partials/table.blade.php b/resources/views/boards/partials/table.blade.php index a28ffa2b..ad2ae824 100644 --- a/resources/views/boards/partials/table.blade.php +++ b/resources/views/boards/partials/table.blade.php @@ -69,14 +69,12 @@ class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 @if($board->trashed()) - - @if(auth()->user()?->is_super_admin) + + @if(auth()->user()?->is_super_admin) - @else - 삭제됨 @endif @else diff --git a/resources/views/departments/partials/table.blade.php b/resources/views/departments/partials/table.blade.php index ea606150..47ed5594 100644 --- a/resources/views/departments/partials/table.blade.php +++ b/resources/views/departments/partials/table.blade.php @@ -44,16 +44,14 @@ @if($department->trashed()) - - @if(auth()->user()?->is_super_admin) + + @if(auth()->user()?->is_super_admin) - @else - 삭제됨 @endif @else diff --git a/resources/views/menus/partials/table.blade.php b/resources/views/menus/partials/table.blade.php index 6bb3d142..dd658313 100644 --- a/resources/views/menus/partials/table.blade.php +++ b/resources/views/menus/partials/table.blade.php @@ -95,18 +95,16 @@ class="relative inline-flex h-4 w-8 items-center rounded-full transition-colors @if($menu->deleted_at) - - @if(auth()->user()?->is_super_admin) + + @if(auth()->user()?->is_super_admin) - @else - 삭제됨 @endif @else diff --git a/resources/views/project-management/projects/partials/table.blade.php b/resources/views/project-management/projects/partials/table.blade.php index 1825fbbc..f4866607 100644 --- a/resources/views/project-management/projects/partials/table.blade.php +++ b/resources/views/project-management/projects/partials/table.blade.php @@ -92,8 +92,7 @@ @if($project->deleted_at) - - @if(auth()->user()?->is_super_admin) +
+ @if(auth()->user()?->is_super_admin) + @endif
- @else - 삭제됨 - @endif @else
diff --git a/resources/views/tenants/partials/table.blade.php b/resources/views/tenants/partials/table.blade.php index 95e47d8e..b5fcf06c 100644 --- a/resources/views/tenants/partials/table.blade.php +++ b/resources/views/tenants/partials/table.blade.php @@ -84,18 +84,16 @@ @if($tenant->deleted_at) - - @if(auth()->user()?->is_super_admin) + + @if(auth()->user()?->is_super_admin) - @else - 삭제됨 @endif @else diff --git a/resources/views/users/partials/modal-info.blade.php b/resources/views/users/partials/modal-info.blade.php index 1ff6af1d..9c2bce9f 100644 --- a/resources/views/users/partials/modal-info.blade.php +++ b/resources/views/users/partials/modal-info.blade.php @@ -18,7 +18,8 @@

- @if(auth()->user()?->is_super_admin) + {{-- 슈퍼관리자이거나, 대상이 일반사용자면 복원 가능 --}} + @if(auth()->user()?->is_super_admin || ! $user->is_super_admin)
+ @endif + @if(auth()->user()?->is_super_admin) - @else + @endif + @if(!$canModify && !auth()->user()?->is_super_admin) 삭제됨 @endif @elseif($canModify) diff --git a/routes/api.php b/routes/api.php index c2a1e66c..0189c590 100644 --- a/routes/api.php +++ b/routes/api.php @@ -39,9 +39,11 @@ Route::put('/{id}', [TenantController::class, 'update'])->name('update'); Route::delete('/{id}', [TenantController::class, 'destroy'])->name('destroy'); - // 슈퍼관리자 전용 액션 + // 복원 (일반관리자 가능) + Route::post('/{id}/restore', [TenantController::class, 'restore'])->name('restore'); + + // 슈퍼관리자 전용 액션 (영구삭제) Route::middleware('super.admin')->group(function () { - Route::post('/{id}/restore', [TenantController::class, 'restore'])->name('restore'); Route::delete('/{id}/force', [TenantController::class, 'forceDestroy'])->name('forceDestroy'); }); @@ -71,9 +73,11 @@ Route::put('/{id}', [DepartmentController::class, 'update'])->name('update'); Route::delete('/{id}', [DepartmentController::class, 'destroy'])->name('destroy'); - // 슈퍼관리자 전용 액션 + // 복원 (일반관리자 가능) + Route::post('/{id}/restore', [DepartmentController::class, 'restore'])->name('restore'); + + // 슈퍼관리자 전용 액션 (영구삭제) Route::middleware('super.admin')->group(function () { - Route::post('/{id}/restore', [DepartmentController::class, 'restore'])->name('restore'); Route::delete('/{id}/force', [DepartmentController::class, 'forceDelete'])->name('forceDelete'); }); }); @@ -86,9 +90,11 @@ Route::put('/{id}', [UserController::class, 'update'])->name('update'); Route::delete('/{id}', [UserController::class, 'destroy'])->name('destroy'); - // 슈퍼관리자 전용 액션 + // 복원 (일반관리자 가능 - 슈퍼관리자 복원은 컨트롤러에서 차단) + Route::post('/{id}/restore', [UserController::class, 'restore'])->name('restore'); + + // 슈퍼관리자 전용 액션 (영구삭제) Route::middleware('super.admin')->group(function () { - Route::post('/{id}/restore', [UserController::class, 'restore'])->name('restore'); Route::delete('/{id}/force', [UserController::class, 'forceDestroy'])->name('forceDestroy'); }); @@ -108,9 +114,11 @@ Route::put('/{id}', [MenuController::class, 'update'])->name('update'); Route::delete('/{id}', [MenuController::class, 'destroy'])->name('destroy'); - // 슈퍼관리자 전용 액션 + // 복원 (일반관리자 가능) + Route::post('/{id}/restore', [MenuController::class, 'restore'])->name('restore'); + + // 슈퍼관리자 전용 액션 (영구삭제) Route::middleware('super.admin')->group(function () { - Route::post('/{id}/restore', [MenuController::class, 'restore'])->name('restore'); Route::delete('/{id}/force', [MenuController::class, 'forceDestroy'])->name('forceDestroy'); }); @@ -140,9 +148,11 @@ Route::put('/{id}', [BoardController::class, 'update'])->name('update'); Route::delete('/{id}', [BoardController::class, 'destroy'])->name('destroy'); - // 슈퍼관리자 전용 액션 + // 복원 (일반관리자 가능) + Route::post('/{id}/restore', [BoardController::class, 'restore'])->name('restore'); + + // 슈퍼관리자 전용 액션 (영구삭제) Route::middleware('super.admin')->group(function () { - Route::post('/{id}/restore', [BoardController::class, 'restore'])->name('restore'); Route::delete('/{id}/force', [BoardController::class, 'forceDestroy'])->name('forceDestroy'); }); @@ -221,9 +231,11 @@ Route::put('/{id}', [PmProjectController::class, 'update'])->name('update'); Route::delete('/{id}', [PmProjectController::class, 'destroy'])->name('destroy'); - // 슈퍼관리자 전용 액션 + // 복원 (일반관리자 가능) + Route::post('/{id}/restore', [PmProjectController::class, 'restore'])->name('restore'); + + // 슈퍼관리자 전용 액션 (영구삭제) Route::middleware('super.admin')->group(function () { - Route::post('/{id}/restore', [PmProjectController::class, 'restore'])->name('restore'); Route::delete('/{id}/force', [PmProjectController::class, 'forceDestroy'])->name('forceDestroy'); }); @@ -245,9 +257,11 @@ Route::put('/{id}', [PmTaskController::class, 'update'])->name('update'); Route::delete('/{id}', [PmTaskController::class, 'destroy'])->name('destroy'); - // 슈퍼관리자 전용 액션 + // 복원 (일반관리자 가능) + Route::post('/{id}/restore', [PmTaskController::class, 'restore'])->name('restore'); + + // 슈퍼관리자 전용 액션 (영구삭제) Route::middleware('super.admin')->group(function () { - Route::post('/{id}/restore', [PmTaskController::class, 'restore'])->name('restore'); Route::delete('/{id}/force', [PmTaskController::class, 'forceDestroy'])->name('forceDestroy'); }); @@ -275,9 +289,11 @@ Route::put('/{id}', [PmIssueController::class, 'update'])->name('update'); Route::delete('/{id}', [PmIssueController::class, 'destroy'])->name('destroy'); - // 슈퍼관리자 전용 액션 + // 복원 (일반관리자 가능) + Route::post('/{id}/restore', [PmIssueController::class, 'restore'])->name('restore'); + + // 슈퍼관리자 전용 액션 (영구삭제) Route::middleware('super.admin')->group(function () { - Route::post('/{id}/restore', [PmIssueController::class, 'restore'])->name('restore'); Route::delete('/{id}/force', [PmIssueController::class, 'forceDestroy'])->name('forceDestroy'); });