- api.php를 13개 도메인별 파일로 분리 (1,479줄 → 61줄) - ApiVersionMiddleware 생성 (헤더/쿼리 기반 버전 선택) - v2 요청 시 v2 없으면 v1으로 자동 폴백 - 지원 헤더: Accept-Version, X-API-Version, api_version 쿼리 분리된 도메인: auth, admin, users, tenants, hr, finance, sales, inventory, production, design, files, boards, common Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
229 lines
15 KiB
PHP
229 lines
15 KiB
PHP
<?php
|
|
|
|
/**
|
|
* 공통 API 라우트 (v1)
|
|
*
|
|
* - 메뉴 관리
|
|
* - 역할/권한 관리
|
|
* - 설정 관리
|
|
* - 카테고리/분류 관리
|
|
* - 푸시 알림
|
|
* - 팝업/리포트/대시보드
|
|
*/
|
|
|
|
use App\Http\Controllers\Api\V1\AiReportController;
|
|
use App\Http\Controllers\Api\V1\CategoryController;
|
|
use App\Http\Controllers\Api\V1\CategoryFieldController;
|
|
use App\Http\Controllers\Api\V1\CategoryLogController;
|
|
use App\Http\Controllers\Api\V1\CategoryTemplateController;
|
|
use App\Http\Controllers\Api\V1\ClassificationController;
|
|
use App\Http\Controllers\Api\V1\CommonController;
|
|
use App\Http\Controllers\Api\V1\DashboardController;
|
|
use App\Http\Controllers\Api\V1\MenuController;
|
|
use App\Http\Controllers\Api\V1\NotificationSettingController;
|
|
use App\Http\Controllers\Api\V1\PayrollController;
|
|
use App\Http\Controllers\Api\V1\PermissionController;
|
|
use App\Http\Controllers\Api\V1\PopupController;
|
|
use App\Http\Controllers\Api\V1\PushNotificationController;
|
|
use App\Http\Controllers\Api\V1\ReportController;
|
|
use App\Http\Controllers\Api\V1\RoleController;
|
|
use App\Http\Controllers\Api\V1\RolePermissionController;
|
|
use App\Http\Controllers\Api\V1\TenantFieldSettingController;
|
|
use App\Http\Controllers\Api\V1\TenantOptionGroupController;
|
|
use App\Http\Controllers\Api\V1\TenantOptionValueController;
|
|
use App\Http\Controllers\Api\V1\WorkSettingController;
|
|
use Illuminate\Support\Facades\Route;
|
|
|
|
// Menu API
|
|
Route::middleware(['perm.map', 'permission'])->prefix('menus')->group(function () {
|
|
Route::get('/', [MenuController::class, 'index'])->name('v1.menus.index');
|
|
Route::post('/', [MenuController::class, 'store'])->name('v1.menus.store');
|
|
Route::post('/reorder', [MenuController::class, 'reorder'])->name('v1.menus.reorder');
|
|
|
|
// 동기화 관련 라우트 (/{id} 전에 위치해야 함)
|
|
Route::get('/trashed', [MenuController::class, 'trashed'])->name('v1.menus.trashed');
|
|
Route::get('/available-global', [MenuController::class, 'availableGlobal'])->name('v1.menus.available-global');
|
|
Route::get('/sync-status', [MenuController::class, 'syncStatus'])->name('v1.menus.sync-status');
|
|
Route::post('/sync', [MenuController::class, 'sync'])->name('v1.menus.sync');
|
|
Route::post('/sync-new', [MenuController::class, 'syncNew'])->name('v1.menus.sync-new');
|
|
Route::post('/sync-updates', [MenuController::class, 'syncUpdates'])->name('v1.menus.sync-updates');
|
|
|
|
// 단일 메뉴 관련 라우트
|
|
Route::get('/{id}', [MenuController::class, 'show'])->name('v1.menus.show');
|
|
Route::patch('/{id}', [MenuController::class, 'update'])->name('v1.menus.update');
|
|
Route::delete('/{id}', [MenuController::class, 'destroy'])->name('v1.menus.destroy');
|
|
Route::post('/{id}/toggle', [MenuController::class, 'toggle'])->name('v1.menus.toggle');
|
|
Route::post('/{id}/restore', [MenuController::class, 'restore'])->name('v1.menus.restore');
|
|
});
|
|
|
|
// Role API
|
|
Route::prefix('roles')->group(function () {
|
|
Route::get('/', [RoleController::class, 'index'])->name('v1.roles.index');
|
|
Route::post('/', [RoleController::class, 'store'])->name('v1.roles.store');
|
|
Route::get('/stats', [RoleController::class, 'stats'])->name('v1.roles.stats');
|
|
Route::get('/active', [RoleController::class, 'active'])->name('v1.roles.active');
|
|
Route::get('/{id}', [RoleController::class, 'show'])->name('v1.roles.show');
|
|
Route::patch('/{id}', [RoleController::class, 'update'])->name('v1.roles.update');
|
|
Route::delete('/{id}', [RoleController::class, 'destroy'])->name('v1.roles.destroy');
|
|
});
|
|
|
|
// Role Permission API - 공통
|
|
Route::get('/role-permissions/menus', [RolePermissionController::class, 'menus'])->name('v1.roles.perms.menus');
|
|
|
|
// Role Permission API - 역할별
|
|
Route::prefix('roles/{id}/permissions')->group(function () {
|
|
Route::get('/', [RolePermissionController::class, 'index'])->name('v1.roles.perms.index');
|
|
Route::post('/', [RolePermissionController::class, 'grant'])->name('v1.roles.perms.grant');
|
|
Route::delete('/', [RolePermissionController::class, 'revoke'])->name('v1.roles.perms.revoke');
|
|
Route::put('/sync', [RolePermissionController::class, 'sync'])->name('v1.roles.perms.sync');
|
|
// 권한 매트릭스 API
|
|
Route::get('/matrix', [RolePermissionController::class, 'matrix'])->name('v1.roles.perms.matrix');
|
|
Route::post('/toggle', [RolePermissionController::class, 'toggle'])->name('v1.roles.perms.toggle');
|
|
Route::post('/allow-all', [RolePermissionController::class, 'allowAll'])->name('v1.roles.perms.allowAll');
|
|
Route::post('/deny-all', [RolePermissionController::class, 'denyAll'])->name('v1.roles.perms.denyAll');
|
|
Route::post('/reset', [RolePermissionController::class, 'reset'])->name('v1.roles.perms.reset');
|
|
});
|
|
|
|
// Permission API
|
|
Route::prefix('permissions')->group(function () {
|
|
Route::get('departments/{dept_id}/menu-matrix', [PermissionController::class, 'deptMenuMatrix'])->name('v1.permissions.deptMenuMatrix');
|
|
Route::get('roles/{role_id}/menu-matrix', [PermissionController::class, 'roleMenuMatrix'])->name('v1.permissions.roleMenuMatrix');
|
|
Route::get('users/{user_id}/menu-matrix', [PermissionController::class, 'userMenuMatrix'])->name('v1.permissions.userMenuMatrix');
|
|
});
|
|
|
|
// Settings & Configuration (설정 및 환경설정 통합 관리)
|
|
Route::prefix('settings')->group(function () {
|
|
// 근무 설정
|
|
Route::get('/work', [WorkSettingController::class, 'showWorkSetting'])->name('v1.settings.work.show');
|
|
Route::put('/work', [WorkSettingController::class, 'updateWorkSetting'])->name('v1.settings.work.update');
|
|
|
|
// 출퇴근 설정
|
|
Route::get('/attendance', [WorkSettingController::class, 'showAttendanceSetting'])->name('v1.settings.attendance.show');
|
|
Route::put('/attendance', [WorkSettingController::class, 'updateAttendanceSetting'])->name('v1.settings.attendance.update');
|
|
|
|
// 급여 설정
|
|
Route::get('/payroll', [PayrollController::class, 'getSettings'])->name('v1.settings.payroll.show');
|
|
Route::put('/payroll', [PayrollController::class, 'updateSettings'])->name('v1.settings.payroll.update');
|
|
|
|
// 테넌트 필드 설정
|
|
Route::get('/fields', [TenantFieldSettingController::class, 'index'])->name('v1.settings.fields.index');
|
|
Route::put('/fields/bulk', [TenantFieldSettingController::class, 'bulkUpsert'])->name('v1.settings.fields.bulk');
|
|
Route::patch('/fields/{key}', [TenantFieldSettingController::class, 'updateOne'])->name('v1.settings.fields.update');
|
|
|
|
// 옵션 그룹/값
|
|
Route::get('/options', [TenantOptionGroupController::class, 'index'])->name('v1.settings.options.index');
|
|
Route::post('/options', [TenantOptionGroupController::class, 'store'])->name('v1.settings.options.store');
|
|
Route::get('/options/{id}', [TenantOptionGroupController::class, 'show'])->name('v1.settings.options.show');
|
|
Route::patch('/options/{id}', [TenantOptionGroupController::class, 'update'])->name('v1.settings.options.update');
|
|
Route::delete('/options/{id}', [TenantOptionGroupController::class, 'destroy'])->name('v1.settings.options.destroy');
|
|
Route::get('/options/{gid}/values', [TenantOptionValueController::class, 'index'])->name('v1.settings.options.values.index');
|
|
Route::post('/options/{gid}/values', [TenantOptionValueController::class, 'store'])->name('v1.settings.options.values.store');
|
|
Route::get('/options/{gid}/values/{id}', [TenantOptionValueController::class, 'show'])->name('v1.settings.options.values.show');
|
|
Route::patch('/options/{gid}/values/{id}', [TenantOptionValueController::class, 'update'])->name('v1.settings.options.values.update');
|
|
Route::delete('/options/{gid}/values/{id}', [TenantOptionValueController::class, 'destroy'])->name('v1.settings.options.values.destroy');
|
|
Route::patch('/options/{gid}/values/reorder', [TenantOptionValueController::class, 'reorder'])->name('v1.settings.options.values.reorder');
|
|
|
|
// 공통 코드 관리
|
|
Route::get('/common/code', [CommonController::class, 'getComeCode'])->name('v1.settings.common.code');
|
|
Route::get('/common', [CommonController::class, 'list'])->name('v1.settings.common.list');
|
|
Route::get('/common/{group}', [CommonController::class, 'index'])->name('v1.settings.common.index');
|
|
Route::post('/common', [CommonController::class, 'store'])->name('v1.settings.common.store');
|
|
Route::patch('/common/{id}', [CommonController::class, 'update'])->name('v1.settings.common.update');
|
|
Route::delete('/common/{id}', [CommonController::class, 'destroy'])->name('v1.settings.common.destroy');
|
|
|
|
// 알림 설정 (그룹 기반) - auth:sanctum 필수
|
|
Route::middleware('auth:sanctum')->group(function () {
|
|
Route::get('/notifications', [NotificationSettingController::class, 'indexGrouped'])->name('v1.settings.notifications.index');
|
|
Route::put('/notifications', [NotificationSettingController::class, 'updateGrouped'])->name('v1.settings.notifications.update');
|
|
});
|
|
});
|
|
|
|
// Push Notification API (FCM) - auth:sanctum 필수
|
|
Route::prefix('push')->middleware('auth:sanctum')->group(function () {
|
|
Route::post('/register-token', [PushNotificationController::class, 'registerToken'])->name('v1.push.register-token');
|
|
Route::post('/unregister-token', [PushNotificationController::class, 'unregisterToken'])->name('v1.push.unregister-token');
|
|
Route::get('/tokens', [PushNotificationController::class, 'getTokens'])->name('v1.push.tokens');
|
|
Route::get('/settings', [PushNotificationController::class, 'getSettings'])->name('v1.push.settings');
|
|
Route::put('/settings', [PushNotificationController::class, 'updateSettings'])->name('v1.push.settings.update');
|
|
Route::get('/notification-types', [PushNotificationController::class, 'getNotificationTypes'])->name('v1.push.notification-types');
|
|
});
|
|
|
|
// Category API (통합)
|
|
Route::prefix('categories')->group(function () {
|
|
// 확장 기능 (와일드카드 라우트보다 먼저 정의)
|
|
Route::get('/tree', [CategoryController::class, 'tree'])->name('v1.categories.tree');
|
|
Route::post('/reorder', [CategoryController::class, 'reorder'])->name('v1.categories.reorder');
|
|
|
|
// 기본 Category CRUD
|
|
Route::get('', [CategoryController::class, 'index'])->name('v1.categories.index');
|
|
Route::post('', [CategoryController::class, 'store'])->name('v1.categories.store');
|
|
Route::get('/{id}', [CategoryController::class, 'show'])->name('v1.categories.show');
|
|
Route::patch('/{id}', [CategoryController::class, 'update'])->name('v1.categories.update');
|
|
Route::delete('/{id}', [CategoryController::class, 'destroy'])->name('v1.categories.destroy');
|
|
Route::post('/{id}/toggle', [CategoryController::class, 'toggle'])->name('v1.categories.toggle');
|
|
Route::patch('/{id}/move', [CategoryController::class, 'move'])->name('v1.categories.move');
|
|
|
|
// Category Fields
|
|
Route::get('/{id}/fields', [CategoryFieldController::class, 'index'])->name('v1.categories.fields.index');
|
|
Route::post('/{id}/fields', [CategoryFieldController::class, 'store'])->name('v1.categories.fields.store');
|
|
Route::get('/fields/{field}', [CategoryFieldController::class, 'show'])->name('v1.categories.fields.show');
|
|
Route::patch('/fields/{field}', [CategoryFieldController::class, 'update'])->name('v1.categories.fields.update');
|
|
Route::delete('/fields/{field}', [CategoryFieldController::class, 'destroy'])->name('v1.categories.fields.destroy');
|
|
Route::post('/{id}/fields/reorder', [CategoryFieldController::class, 'reorder'])->name('v1.categories.fields.reorder');
|
|
Route::put('/{id}/fields/bulk-upsert', [CategoryFieldController::class, 'bulkUpsert'])->name('v1.categories.fields.bulk');
|
|
|
|
// Category Templates
|
|
Route::get('/{id}/templates', [CategoryTemplateController::class, 'index'])->name('v1.categories.templates.index');
|
|
Route::post('/{id}/templates', [CategoryTemplateController::class, 'store'])->name('v1.categories.templates.store');
|
|
Route::get('/templates/{tpl}', [CategoryTemplateController::class, 'show'])->name('v1.categories.templates.show');
|
|
Route::patch('/templates/{tpl}', [CategoryTemplateController::class, 'update'])->name('v1.categories.templates.update');
|
|
Route::delete('/templates/{tpl}', [CategoryTemplateController::class, 'destroy'])->name('v1.categories.templates.destroy');
|
|
Route::post('/{id}/templates/{tpl}/apply', [CategoryTemplateController::class, 'apply'])->name('v1.categories.templates.apply');
|
|
Route::get('/{id}/templates/{tpl}/preview', [CategoryTemplateController::class, 'preview'])->name('v1.categories.templates.preview');
|
|
Route::get('/{id}/templates/diff', [CategoryTemplateController::class, 'diff'])->name('v1.categories.templates.diff');
|
|
|
|
// Category Logs
|
|
Route::get('/{id}/logs', [CategoryLogController::class, 'index'])->name('v1.categories.logs.index');
|
|
Route::get('/logs/{log}', [CategoryLogController::class, 'show'])->name('v1.categories.logs.show');
|
|
});
|
|
|
|
// Classifications API
|
|
Route::prefix('classifications')->group(function () {
|
|
Route::get('', [ClassificationController::class, 'index'])->name('v1.classifications.index');
|
|
Route::post('', [ClassificationController::class, 'store'])->name('v1.classifications.store');
|
|
Route::get('/{id}', [ClassificationController::class, 'show'])->whereNumber('id')->name('v1.classifications.show');
|
|
Route::patch('/{id}', [ClassificationController::class, 'update'])->whereNumber('id')->name('v1.classifications.update');
|
|
Route::delete('/{id}', [ClassificationController::class, 'destroy'])->whereNumber('id')->name('v1.classifications.destroy');
|
|
});
|
|
|
|
// Popup API (팝업관리)
|
|
Route::prefix('popups')->group(function () {
|
|
Route::get('', [PopupController::class, 'index'])->name('v1.popups.index');
|
|
Route::post('', [PopupController::class, 'store'])->name('v1.popups.store');
|
|
Route::get('/active', [PopupController::class, 'active'])->name('v1.popups.active');
|
|
Route::get('/{id}', [PopupController::class, 'show'])->whereNumber('id')->name('v1.popups.show');
|
|
Route::put('/{id}', [PopupController::class, 'update'])->whereNumber('id')->name('v1.popups.update');
|
|
Route::delete('/{id}', [PopupController::class, 'destroy'])->whereNumber('id')->name('v1.popups.destroy');
|
|
});
|
|
|
|
// Report API (보고서)
|
|
Route::prefix('reports')->group(function () {
|
|
Route::get('/daily', [ReportController::class, 'daily'])->name('v1.reports.daily');
|
|
Route::get('/daily/export', [ReportController::class, 'dailyExport'])->name('v1.reports.daily.export');
|
|
Route::get('/expense-estimate', [ReportController::class, 'expenseEstimate'])->name('v1.reports.expense-estimate');
|
|
Route::get('/expense-estimate/export', [ReportController::class, 'expenseEstimateExport'])->name('v1.reports.expense-estimate.export');
|
|
|
|
// AI Report API
|
|
Route::get('/ai', [AiReportController::class, 'index'])->name('v1.reports.ai.index');
|
|
Route::post('/ai/generate', [AiReportController::class, 'generate'])->name('v1.reports.ai.generate');
|
|
Route::get('/ai/{id}', [AiReportController::class, 'show'])->whereNumber('id')->name('v1.reports.ai.show');
|
|
Route::delete('/ai/{id}', [AiReportController::class, 'destroy'])->whereNumber('id')->name('v1.reports.ai.destroy');
|
|
});
|
|
|
|
// Dashboard API (대시보드)
|
|
Route::prefix('dashboard')->group(function () {
|
|
Route::get('/summary', [DashboardController::class, 'summary'])->name('v1.dashboard.summary');
|
|
Route::get('/charts', [DashboardController::class, 'charts'])->name('v1.dashboard.charts');
|
|
Route::get('/approvals', [DashboardController::class, 'approvals'])->name('v1.dashboard.approvals');
|
|
});
|