Files
sam-api/routes/console.php
김보곤 1eb8d2cb01 feat: [demo] 데모 테넌트 운영 자동화 (Phase 2)
- DemoLimitMiddleware: 쇼케이스 읽기전용, 만료 체크, 외부연동 차단
- DemoTenantService: 파트너/체험 테넌트 생성, 기간 연장, 정식 전환
- ResetDemoShowcaseCommand: 매일 자정 데이터 리셋 + 샘플 재시드
- ManufacturingPresetSeeder: 부서/거래처/품목/견적/수주 샘플 데이터
- 스케줄러 등록 (00:00 demo:reset-showcase --seed)
- 미들웨어 별칭 등록 (demo.limit)
2026-03-14 14:41:55 +09:00

172 lines
7.4 KiB
PHP

<?php
use App\Models\ApiRequestLog;
use Illuminate\Foundation\Inspiring;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Schedule;
Artisan::command('inspire', function () {
$this->comment(Inspiring::quote());
})->purpose('Display an inspiring quote');
// API 요청 로그 정리 커맨드
Artisan::command('api-log:prune', function () {
$deleted = ApiRequestLog::pruneOldLogs();
$this->info("{$deleted}개의 API 요청 로그가 삭제되었습니다.");
})->purpose('하루 지난 API 요청 로그 삭제');
// 스케줄러 정의 (Laravel 12 표준 방식)
// 매일 새벽 03:00에 API 요청 로그 정리 (하루치만 보관)
Schedule::command('api-log:prune')
->dailyAt('03:00')
->appendOutputTo(storage_path('logs/scheduler.log'))
->onSuccess(function () {
\Illuminate\Support\Facades\Log::info('✅ api-log:prune 스케줄러 실행 성공', ['time' => now()]);
})
->onFailure(function () {
\Illuminate\Support\Facades\Log::error('❌ api-log:prune 스케줄러 실행 실패', ['time' => now()]);
});
// 매일 새벽 03:10에 감사 로그 정리 (환경값 기반 보관기간)
Schedule::command('audit:prune')
->dailyAt('03:10')
->appendOutputTo(storage_path('logs/scheduler.log'))
->onSuccess(function () {
\Illuminate\Support\Facades\Log::info('✅ audit:prune 스케줄러 실행 성공', ['time' => now()]);
})
->onFailure(function () {
\Illuminate\Support\Facades\Log::error('❌ audit:prune 스케줄러 실행 실패', ['time' => now()]);
});
// 매일 새벽 03:20에 만료된 토큰 정리 (만료 후 24시간 보관)
Schedule::command('sanctum:prune-expired --hours=24')
->dailyAt('03:20')
->appendOutputTo(storage_path('logs/scheduler.log'))
->onSuccess(function () {
\Illuminate\Support\Facades\Log::info('✅ sanctum:prune-expired 스케줄러 실행 성공', ['time' => now()]);
})
->onFailure(function () {
\Illuminate\Support\Facades\Log::error('❌ sanctum:prune-expired 스케줄러 실행 실패', ['time' => now()]);
});
// 매일 새벽 03:30에 7일 이상 된 임시 파일 정리
Schedule::command('storage:cleanup-temp')
->dailyAt('03:30')
->appendOutputTo(storage_path('logs/scheduler.log'))
->onSuccess(function () {
\Illuminate\Support\Facades\Log::info('✅ storage:cleanup-temp 스케줄러 실행 성공', ['time' => now()]);
})
->onFailure(function () {
\Illuminate\Support\Facades\Log::error('❌ storage:cleanup-temp 스케줄러 실행 실패', ['time' => now()]);
});
// 매일 새벽 03:40에 휴지통 파일 영구 삭제 (30일 이상)
Schedule::command('storage:cleanup-trash')
->dailyAt('03:40')
->appendOutputTo(storage_path('logs/scheduler.log'))
->onSuccess(function () {
\Illuminate\Support\Facades\Log::info('✅ storage:cleanup-trash 스케줄러 실행 성공', ['time' => now()]);
})
->onFailure(function () {
\Illuminate\Support\Facades\Log::error('❌ storage:cleanup-trash 스케줄러 실행 실패', ['time' => now()]);
});
// 매일 새벽 03:50에 만료된 공유 링크 정리
Schedule::command('storage:cleanup-links')
->dailyAt('03:50')
->appendOutputTo(storage_path('logs/scheduler.log'))
->onSuccess(function () {
\Illuminate\Support\Facades\Log::info('✅ storage:cleanup-links 스케줄러 실행 성공', ['time' => now()]);
})
->onFailure(function () {
\Illuminate\Support\Facades\Log::error('❌ storage:cleanup-links 스케줄러 실행 실패', ['time' => now()]);
});
// 매일 새벽 04:00에 용량 사용 히스토리 기록
Schedule::command('storage:record-usage')
->dailyAt('04:00')
->appendOutputTo(storage_path('logs/scheduler.log'))
->onSuccess(function () {
\Illuminate\Support\Facades\Log::info('✅ storage:record-usage 스케줄러 실행 성공', ['time' => now()]);
})
->onFailure(function () {
\Illuminate\Support\Facades\Log::error('❌ storage:record-usage 스케줄러 실행 실패', ['time' => now()]);
});
// ─── 통계 집계 (sam_stat DB) ───
// 매일 새벽 02:00에 일간 통계 집계 (전일 데이터)
Schedule::command('stat:aggregate-daily')
->dailyAt('02:00')
->withoutOverlapping()
->appendOutputTo(storage_path('logs/scheduler.log'))
->onSuccess(function () {
\Illuminate\Support\Facades\Log::info('✅ stat:aggregate-daily 스케줄러 실행 성공', ['time' => now()]);
})
->onFailure(function () {
\Illuminate\Support\Facades\Log::error('❌ stat:aggregate-daily 스케줄러 실행 실패', ['time' => now()]);
});
// 매월 1일 새벽 03:00에 월간 통계 집계 (전월 데이터)
Schedule::command('stat:aggregate-monthly')
->monthlyOn(1, '03:00')
->withoutOverlapping()
->appendOutputTo(storage_path('logs/scheduler.log'))
->onSuccess(function () {
\Illuminate\Support\Facades\Log::info('✅ stat:aggregate-monthly 스케줄러 실행 성공', ['time' => now()]);
})
->onFailure(function () {
\Illuminate\Support\Facades\Log::error('❌ stat:aggregate-monthly 스케줄러 실행 실패', ['time' => now()]);
});
// ─── DB 백업 모니터링 ───
// 매일 새벽 05:00에 DB 백업 상태 확인 (04:30 백업 완료 후 점검)
Schedule::command('db:backup-check')
->dailyAt('05:00')
->appendOutputTo(storage_path('logs/scheduler.log'))
->onSuccess(function () {
\Illuminate\Support\Facades\Log::info('✅ db:backup-check 스케줄러 실행 성공', ['time' => now()]);
})
->onFailure(function () {
\Illuminate\Support\Facades\Log::error('❌ db:backup-check 스케줄러 실행 실패', ['time' => now()]);
});
// ─── 트리거 감사 로그 파티션 관리 ───
// 매월 1일 새벽 04:10에 파티션 관리 (3개월 미래 추가 + 13개월 초과 삭제)
Schedule::command('audit:partitions --add-months=3 --retention-months=13 --drop')
->monthlyOn(1, '04:10')
->appendOutputTo(storage_path('logs/scheduler.log'))
->onSuccess(function () {
\Illuminate\Support\Facades\Log::info('✅ audit:partitions 스케줄러 실행 성공', ['time' => now()]);
})
->onFailure(function () {
\Illuminate\Support\Facades\Log::error('❌ audit:partitions 스케줄러 실행 실패', ['time' => now()]);
});
// 매일 오전 09:00에 KPI 목표 대비 알림 체크
Schedule::command('stat:check-kpi-alerts')
->dailyAt('09:00')
->appendOutputTo(storage_path('logs/scheduler.log'))
->onSuccess(function () {
\Illuminate\Support\Facades\Log::info('✅ stat:check-kpi-alerts 스케줄러 실행 성공', ['time' => now()]);
})
->onFailure(function () {
\Illuminate\Support\Facades\Log::error('❌ stat:check-kpi-alerts 스케줄러 실행 실패', ['time' => now()]);
});
// ─── 데모 쇼케이스 리셋 ───
// 매일 자정 00:00에 쇼케이스 테넌트 데이터 리셋 + 샘플 재시드
Schedule::command('demo:reset-showcase --seed')
->dailyAt('00:00')
->appendOutputTo(storage_path('logs/scheduler.log'))
->onSuccess(function () {
\Illuminate\Support\Facades\Log::info('✅ demo:reset-showcase 스케줄러 실행 성공', ['time' => now()]);
})
->onFailure(function () {
\Illuminate\Support\Facades\Log::error('❌ demo:reset-showcase 스케줄러 실행 실패', ['time' => now()]);
});