Files
sam-api/routes/api/v1/common.php
김보곤 240199af9d chore: [env] .env.example 업데이트 및 .gitignore 정리
- .env.example을 SAM 프로젝트 실제 키 구조로 업데이트
- .gitignore에 !.env.example 예외 추가
- GCS_* 중복 키 제거, Gemini/Claude/Vertex 키 섹션 추가
2026-02-23 10:17:37 +09:00

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');
});