cron 스케줄러 한국 시간대 적용 및 봇 카드 UI 개선

- scheduler.js: cron.schedule에 timezone: 'Asia/Seoul' 옵션 추가
- bots.js: Meilisearch 봇 API에 버전 정보 추가
- BotCard.jsx: Meilisearch 봇 카드에 마지막 동기화 시간, 동기화 수, 버전 표시

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
caadiq 2026-01-23 21:52:01 +09:00
parent 8091f4ac67
commit 95285634e9
3 changed files with 37 additions and 14 deletions

View file

@ -4,6 +4,7 @@ import bots from '../config/bots.js';
import { syncWithRetry, getVersion } from '../services/meilisearch/index.js'; import { syncWithRetry, getVersion } from '../services/meilisearch/index.js';
const REDIS_PREFIX = 'bot:status:'; const REDIS_PREFIX = 'bot:status:';
const TIMEZONE = 'Asia/Seoul';
async function schedulerPlugin(fastify, opts) { async function schedulerPlugin(fastify, opts) {
const tasks = new Map(); const tasks = new Map();
@ -60,7 +61,7 @@ async function schedulerPlugin(fastify, opts) {
const CHECK_INTERVAL = 60 * 1000; // 1분 const CHECK_INTERVAL = 60 * 1000; // 1분
const CHECK_DURATION = 5 * 60 * 1000; // 5분간 체크 const CHECK_DURATION = 5 * 60 * 1000; // 5분간 체크
// 체크 시작 cron (매일 4시) // 체크 시작 cron (매일 4시 KST)
const task = cron.schedule(bot.cron, async () => { const task = cron.schedule(bot.cron, async () => {
fastify.log.info(`[${botId}] 버전 체크 시작 (5분간 1분 간격)`); fastify.log.info(`[${botId}] 버전 체크 시작 (5분간 1분 간격)`);
await updateStatus(botId, { status: 'running' }); await updateStatus(botId, { status: 'running' });
@ -114,7 +115,7 @@ async function schedulerPlugin(fastify, opts) {
await performSync(botId, currentVersion, REDIS_VERSION_KEY); await performSync(botId, currentVersion, REDIS_VERSION_KEY);
} }
}, CHECK_INTERVAL); }, CHECK_INTERVAL);
}); }, { timezone: TIMEZONE });
tasks.set(botId, task); tasks.set(botId, task);
await updateStatus(botId, { status: 'running' }); await updateStatus(botId, { status: 'running' });
@ -202,7 +203,7 @@ async function schedulerPlugin(fastify, opts) {
throw new Error(`지원하지 않는 봇 타입: ${bot.type}`); throw new Error(`지원하지 않는 봇 타입: ${bot.type}`);
} }
// cron 태스크 등록 // cron 태스크 등록 (한국 시간 기준)
const task = cron.schedule(bot.cron, async () => { const task = cron.schedule(bot.cron, async () => {
fastify.log.info(`[${botId}] 동기화 시작`); fastify.log.info(`[${botId}] 동기화 시작`);
try { try {
@ -217,7 +218,7 @@ async function schedulerPlugin(fastify, opts) {
}); });
fastify.log.error(`[${botId}] 동기화 오류: ${err.message}`); fastify.log.error(`[${botId}] 동기화 오류: ${err.message}`);
} }
}); }, { timezone: TIMEZONE });
tasks.set(botId, task); tasks.set(botId, task);
await updateStatus(botId, { status: 'running' }); await updateStatus(botId, { status: 'running' });

View file

@ -67,7 +67,7 @@ export default async function botsRoutes(fastify) {
checkInterval = parseInt(cronMatch[1]); checkInterval = parseInt(cronMatch[1]);
} }
result.push({ const botData = {
id: bot.id, id: bot.id,
name: bot.name || bot.channelName || bot.username || bot.id, name: bot.name || bot.channelName || bot.username || bot.id,
type: bot.type, type: bot.type,
@ -78,7 +78,15 @@ export default async function botsRoutes(fastify) {
check_interval: checkInterval, check_interval: checkInterval,
error_message: status.errorMessage, error_message: status.errorMessage,
enabled: bot.enabled, enabled: bot.enabled,
}); };
// Meilisearch 봇인 경우 버전 정보 추가
if (bot.type === 'meilisearch') {
const version = await redis.get('meilisearch:version');
botData.version = version || '-';
}
result.push(botData);
} }
return result; return result;

View file

@ -145,14 +145,28 @@ const BotCard = memo(function BotCard({
{bot.type === 'meilisearch' ? ( {bot.type === 'meilisearch' ? (
<> <>
<div className="p-3 text-center"> <div className="p-3 text-center">
<div className="text-lg font-bold text-gray-900">{bot.schedules_added || 0}</div> <div className="text-lg font-bold text-gray-900">
<div className="text-xs text-gray-400">동기화 </div> {bot.last_check_at
? new Date(bot.last_check_at).toLocaleString('ko-KR', {
month: 'numeric',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
hour12: false,
})
: '-'}
</div>
<div className="text-xs text-gray-400">마지막 동기화</div>
</div> </div>
<div className="p-3 text-center"> <div className="p-3 text-center">
<div className="text-lg font-bold text-gray-900"> <div className="text-lg font-bold text-gray-900">
{bot.last_added_count ? `${(bot.last_added_count / 1000 || 0).toFixed(1)}` : '-'} {bot.last_added_count?.toLocaleString() || '-'}
</div> </div>
<div className="text-xs text-gray-400">소요 시간</div> <div className="text-xs text-gray-400">동기화 </div>
</div>
<div className="p-3 text-center">
<div className="text-lg font-bold text-gray-900">{bot.version || '-'}</div>
<div className="text-xs text-gray-400">버전</div>
</div> </div>
</> </>
) : ( ) : (
@ -169,12 +183,12 @@ const BotCard = memo(function BotCard({
</div> </div>
<div className="text-xs text-gray-400">마지막</div> <div className="text-xs text-gray-400">마지막</div>
</div> </div>
<div className="p-3 text-center">
<div className="text-lg font-bold text-gray-900">{formatInterval(bot.check_interval)}</div>
<div className="text-xs text-gray-400">업데이트 간격</div>
</div>
</> </>
)} )}
<div className="p-3 text-center">
<div className="text-lg font-bold text-gray-900">{formatInterval(bot.check_interval)}</div>
<div className="text-xs text-gray-400">업데이트 간격</div>
</div>
</div> </div>
{/* 오류 메시지 */} {/* 오류 메시지 */}