group(function () { // 로그인/OTP/비번초기화는 guest:admin 만 접근 Route::middleware('guest:admin')->group(function () { Route::get('/login', [AdminAuthController::class, 'showLogin']) ->name('admin.login.form'); Route::post('/login', [AdminAuthController::class, 'storeLogin']) ->middleware('throttle:admin-login') ->name('admin.login.store'); Route::get('/password/reset', [AdminAuthController::class, 'showForceReset']) ->name('admin.password.reset.form'); Route::post('/password/reset', [AdminAuthController::class, 'storeForceReset']) ->middleware('throttle:admin-login') ->name('admin.password.reset.store'); // OTP(sms) 2차 인증 Route::get('/otp', [AdminAuthController::class, 'showOtp']) ->name('admin.otp.form'); Route::post('/otp', [AdminAuthController::class, 'verifyOtp']) ->middleware('throttle:admin-otp') ->name('admin.otp.store'); // TOTP(구글 OTP) 2차 인증 Route::get('/totp', [AdminAuthController::class, 'showTotp']) ->name('admin.totp.form'); Route::post('/totp', [AdminAuthController::class, 'verifyTotp']) ->middleware('throttle:admin-otp') // 필요하면 throttle:admin-totp 로 분리 가능 ->name('admin.totp.store'); }); // 로그인 이후 Route::middleware(['auth:admin', \App\Http\Middleware\NoStore::class])->group(function () { // 대시보드: 전체 허용 Route::get('/', fn() => view('admin.home'))->name('admin.home'); // 내 정보: 전체 허용 Route::get('/me', [MeController::class, 'show'])->name('admin.me'); Route::post('/me', [MeController::class, 'update'])->name('admin.me.update'); Route::get('/me/password', [MeController::class, 'showPassword'])->name('admin.me.password.form'); Route::post('/me/password', [MeController::class, 'updatePassword'])->name('admin.me.password.update'); // 보안/OTP 등록(자기계정 설정): 전체 허용 Route::get('/security', [AdminAuthController::class, 'security'])->name('admin.security'); Route::post('/totp/start', [AdminAuthController::class, 'totpStart'])->name('admin.totp.start'); Route::post('/totp/confirm', [AdminAuthController::class, 'totpConfirm'])->name('admin.totp.confirm'); Route::post('/totp/disable', [AdminAuthController::class, 'totpDisable'])->name('admin.totp.disable'); Route::post('/totp/reset', [AdminAuthController::class, 'totpReset'])->name('admin.totp.reset'); // 재등록(새 시크릿) Route::post('/totp/mode', [AdminAuthController::class, 'totpMode'])->name('admin.totp.mode'); Route::post('/logout', [AdminAuthController::class, 'logout'])->name('admin.logout'); // 관리자 계정 관리: super_admin 전용 Route::prefix('/admins') ->name('admin.admins.') ->middleware('admin.role:super_admin') ->group(function () { Route::get('/', [AdminAdminsController::class, 'index'])->name('index'); Route::get('/create', [AdminAdminsController::class, 'create'])->name('create'); Route::post('/', [AdminAdminsController::class, 'store'])->name('store'); Route::get('/{id}', [AdminAdminsController::class, 'edit'])->name('edit'); Route::post('/{id}', [AdminAdminsController::class, 'update'])->name('update'); Route::post('/{id}/reset-password', [AdminAdminsController::class, 'resetPassword'])->name('reset_password'); Route::post('/{id}/unlock', [AdminAdminsController::class, 'unlock'])->name('unlock'); }); Route::prefix('/sms')->group(function () { // 발송 Route::get('/send', [AdminSmsController::class, 'create'])->name('admin.sms.send'); Route::post('/send', [AdminSmsController::class, 'store'])->name('admin.sms.send.store'); // 이력 Route::get('/logs', [AdminSmsLogController::class, 'index'])->name('admin.sms.logs'); Route::get('/logs/{batchId}', [AdminSmsLogController::class, 'show'])->name('admin.sms.logs.show'); }); Route::prefix('/templates')->name('admin.templates.')->group(function () { Route::get('/', [AdminSmsTemplateController::class, 'index']) ->name('index'); Route::get('/create', [AdminSmsTemplateController::class, 'create']) ->name('create'); Route::post('/', [AdminSmsTemplateController::class, 'store']) ->name('store'); Route::get('/{id}', [AdminSmsTemplateController::class, 'edit']) ->whereNumber('id') ->name('edit'); Route::put('/{id}', [AdminSmsTemplateController::class, 'update']) ->whereNumber('id') ->name('update'); }); Route::prefix('/mail')->group(function () { // 발송 Route::get('/send', [AdminMailController::class, 'create'])->name('admin.mail.send'); Route::post('/send', [AdminMailController::class, 'store'])->name('admin.mail.send.store'); // 로그 Route::get('/logs', [AdminMailLogController::class, 'index'])->name('admin.mail.logs'); Route::get('/logs/{batchId}', [AdminMailLogController::class, 'show'])->name('admin.mail.logs.show'); Route::post('/logs/{batchId}/cancel', [AdminMailLogController::class, 'cancel'])->name('admin.mail.logs.cancel'); Route::post('/logs/{batchId}/retry-failed', [AdminMailLogController::class, 'retryFailed'])->name('admin.mail.logs.retry_failed'); // 템플릿 Route::get('/templates', [AdminMailTemplateController::class, 'index'])->name('admin.mail.templates.index'); Route::get('/templates/create', [AdminMailTemplateController::class, 'create'])->name('admin.mail.templates.create'); Route::post('/templates', [AdminMailTemplateController::class, 'store'])->name('admin.mail.templates.store'); Route::get('/templates/{id}/edit', [AdminMailTemplateController::class, 'edit'])->name('admin.mail.templates.edit'); Route::put('/templates/{id}', [AdminMailTemplateController::class, 'update'])->name('admin.mail.templates.update'); Route::post('/preview', [AdminMailController::class, 'preview'])->name('admin.mail.preview'); }); Route::prefix('/notice') ->name('admin.notice.') ->middleware('admin.role:super_admin,support') ->group(function () { Route::get('/', [AdminNoticeController::class, 'index'])->name('index'); Route::get('/create', [AdminNoticeController::class, 'create'])->name('create'); Route::post('/', [AdminNoticeController::class, 'store'])->name('store'); Route::get('/{id}', [AdminNoticeController::class, 'edit'])->whereNumber('id')->name('edit'); Route::put('/{id}', [AdminNoticeController::class, 'update'])->whereNumber('id')->name('update'); Route::delete('/{id}', [AdminNoticeController::class, 'destroy'])->whereNumber('id')->name('destroy'); Route::get('/{id}/file/{slot}', [AdminNoticeController::class, 'download']) ->whereNumber('id') ->whereIn('slot', ['1', '2']) ->name('file'); }); Route::prefix('qna') ->name('admin.qna.') ->middleware('admin.role:super_admin,support') ->group(function () { Route::get('/', [AdminQnaController::class, 'index'])->name('index'); Route::get('/{seq}', [AdminQnaController::class, 'show'])->name('show'); // 업무 액션 Route::post('/{seq}/assign', [AdminQnaController::class, 'assignToMe'])->name('assign'); Route::post('/{seq}/start', [AdminQnaController::class, 'startWork'])->name('start'); Route::post('/{seq}/return', [AdminQnaController::class, 'returnWork'])->name('return'); Route::post('/{seq}/postpone', [AdminQnaController::class, 'postponeWork'])->name('postpone'); Route::post('/{seq}/answer', [AdminQnaController::class, 'saveAnswer'])->name('answer.save'); Route::post('/{seq}/complete', [AdminQnaController::class, 'completeWork'])->name('complete'); // 내부 메모(선택) Route::post('/{seq}/memo', [AdminQnaController::class, 'addMemo'])->name('memo.add'); }); Route::prefix('members') ->name('admin.members.') ->middleware('admin.role:super_admin,support') ->group(function () { Route::get('/', [AdminMembersController::class, 'index']) ->name('index'); Route::get('/{memNo}', [AdminMembersController::class, 'show']) ->whereNumber('memNo') ->name('show'); Route::post('/{memNo}', [AdminMembersController::class, 'update']) ->whereNumber('memNo') ->name('update'); Route::post('/{memNo}/memo', [AdminMembersController::class, 'addMemo']) ->whereNumber('memNo') ->name('memo.add'); Route::post('/{memNo}/reset-password', [AdminMembersController::class, 'resetPassword']) ->whereNumber('memNo') ->name('password.reset'); Route::post('/{memNo}/force-out', [AdminMembersController::class, 'forceOut']) ->whereNumber('memNo') ->name('force_out'); }); Route::prefix('marketing') ->name('admin.marketing.') ->middleware('admin.role:super_admin,support') ->group(function () { Route::get('/', [AdminMemberMarketingController::class, 'index']) ->name('index'); Route::post('/export', [AdminMemberMarketingController::class, 'export']) ->name('export'); }); Route::prefix('join-filters') ->name('admin.join-filters.') ->middleware('admin.role:super_admin') ->group(function () { Route::get('/', [AdminMemberJoinFilterController::class, 'index']) ->name('index'); Route::get('/{seq}', [AdminMemberJoinFilterController::class, 'get']) ->whereNumber('seq') ->name('get'); // AJAX row Route::post('/', [AdminMemberJoinFilterController::class, 'store']) ->name('store'); Route::match(['POST','PUT'], '/{seq}', [AdminMemberJoinFilterController::class, 'update']) ->whereNumber('seq') ->name('update'); Route::match(['POST','DELETE'], '/{seq}/delete', [AdminMemberJoinFilterController::class, 'destroy']) ->whereNumber('seq') ->name('destroy'); }); Route::prefix('systemlog') ->name('admin.systemlog.') ->middleware('admin.role:super_admin') ->group(function () { Route::get('/admin-audit-logs', [AdminAuditLogController::class, 'index']) ->name('admin-audit-logs'); Route::get('/admin-audit-logs/{id}', [AdminAuditLogController::class, 'show']) ->whereNumber('id') ->name('admin-audit-logs.show'); Route::get('/member-join-logs', [MemberJoinLogController::class, 'index']) ->name('member-join-logs'); Route::get('/member-login-logs', [MemberLoginLogController::class, 'index']) ->name('member-login-logs'); Route::get('/member-passwd-modify-logs', [MemberPasswdModifyLogController::class, 'index']) ->name('member-passwd-modify-logs'); Route::get('/member-account-logs', [MemberAccountLogController::class, 'index']) ->name('member-account-logs'); Route::get('/member-danalauthtel-logs', [MemberDanalAuthTelLogController::class, 'index']) ->name('member-danalauthtel-logs'); }); Route::prefix('categories')->name('admin.categories.') ->middleware('admin.role:super_admin,product') ->group(function () { Route::get('/', [AdminCategoryController::class, 'index'])->name('index'); Route::post('/', [AdminCategoryController::class, 'store'])->name('store'); Route::put('/{id}', [AdminCategoryController::class, 'update'])->whereNumber('id')->name('update'); Route::post('/sort', [AdminCategoryController::class, 'updateSort'])->name('sort'); Route::delete('/{id}', [AdminCategoryController::class, 'destroy'])->whereNumber('id')->name('destroy'); }); Route::prefix('fees')->name('admin.fees.') ->middleware('admin.role:super_admin,product') ->group(function () { // 통합 화면 Route::get('/', [AdminFeeController::class, 'index'])->name('index'); // 매입(출금) 정책 수정 Route::put('/buyback', [AdminFeeController::class, 'updateBuybackPolicy'])->name('buyback.update'); // 결제 수단 (Payment) CRUD & 정렬 Route::post('/payment', [AdminFeeController::class, 'storePayment'])->name('payment.store'); Route::put('/payment/{id}', [AdminFeeController::class, 'updatePayment'])->whereNumber('id')->name('payment.update'); Route::delete('/payment/{id}', [AdminFeeController::class, 'destroyPayment'])->whereNumber('id')->name('payment.destroy'); Route::post('/payment/sort', [AdminFeeController::class, 'updatePaymentSort'])->name('payment.sort'); }); Route::prefix('sale-codes')->name('admin.sale-codes.') ->middleware('admin.role:super_admin,product') ->group(function () { Route::get('/', [AdminSaleCodeController::class, 'index'])->name('index'); // 연동사(Provider) 라우트 Route::post('/provider', [AdminSaleCodeController::class, 'storeProvider'])->name('provider.store'); Route::put('/provider/{id}', [AdminSaleCodeController::class, 'updateProvider'])->whereNumber('id')->name('provider.update'); Route::delete('/provider/{id}', [AdminSaleCodeController::class, 'destroyProvider'])->whereNumber('id')->name('provider.destroy'); // 상품 코드(Code) 라우트 및 정렬 Route::post('/code', [AdminSaleCodeController::class, 'storeCode'])->name('code.store'); Route::put('/code/{id}', [AdminSaleCodeController::class, 'updateCode'])->whereNumber('id')->name('code.update'); Route::delete('/code/{id}', [AdminSaleCodeController::class, 'destroyCode'])->whereNumber('id')->name('code.destroy'); Route::post('/code/sort', [AdminSaleCodeController::class, 'updateCodeSort'])->name('code.sort'); }); Route::prefix('media')->name('admin.media.')->middleware('admin.role:super_admin,product')->group(function () { Route::get('/', [AdminMediaController::class, 'index'])->name('index'); Route::post('/', [AdminMediaController::class, 'store'])->name('store'); Route::delete('/{id}', [AdminMediaController::class, 'destroy'])->whereNumber('id')->name('destroy'); // 향후 상품 등록 폼의 팝업 모달에서 AJAX로 호출할 API Route::get('/api/list', [AdminMediaController::class, 'apiList'])->name('api.list'); // 기존 미디어 라우트 그룹 내부에 추가 Route::put('/{id}/name', [AdminMediaController::class, 'updateName'])->whereNumber('id')->name('name.update'); }); Route::prefix('products')->name('admin.products.')->middleware('admin.role:super_admin,product')->group(function () { Route::get('/', [AdminProductController::class, 'index'])->name('index'); Route::get('/create', [AdminProductController::class, 'create'])->name('create'); Route::post('/', [AdminProductController::class, 'store'])->name('store'); Route::get('/{id}/edit', [AdminProductController::class, 'edit'])->whereNumber('id')->name('edit'); Route::put('/{id}', [AdminProductController::class, 'update'])->whereNumber('id')->name('update'); }); Route::prefix('products/{productId}/skus/{skuId}/pins')->name('admin.pins.')->middleware('admin.role:super_admin,product')->group(function () { Route::get('/', [AdminPinController::class, 'index'])->name('index'); Route::post('/bulk', [AdminPinController::class, 'storeBulk'])->name('storeBulk'); Route::post('/recall', [AdminPinController::class, 'recallBulk'])->name('recallBulk'); }); }); }); /* 개발용 페이지 세션 보기 */ if (config('app.debug') || app()->environment('local')) { require __DIR__.'/dev_admin.php'; } /* 개발용 페이지 세션 보기 */