fromis_9/backend/src/routes/admin/bots.js

181 lines
4.6 KiB
JavaScript
Raw Normal View History

import bots from '../../config/bots.js';
/**
* 관리 라우트
* 인증 필요
*/
export default async function botsRoutes(fastify) {
const { scheduler, redis } = fastify;
const QUOTA_WARNING_KEY = 'youtube:quota_warning';
/**
* GET /api/admin/bots
* 목록 조회
*/
fastify.get('/', {
schema: {
tags: ['admin/bots'],
summary: '봇 목록 조회',
security: [{ bearerAuth: [] }],
},
preHandler: [fastify.authenticate],
}, async (request, reply) => {
const result = [];
for (const bot of bots) {
const status = await scheduler.getStatus(bot.id);
// cron 표현식에서 간격 추출 (분 단위)
let checkInterval = 2; // 기본값
const cronMatch = bot.cron.match(/^\*\/(\d+)/);
if (cronMatch) {
checkInterval = parseInt(cronMatch[1]);
}
result.push({
id: bot.id,
name: bot.channelName || bot.username || bot.id,
type: bot.type,
status: status.status,
last_check_at: status.lastCheckAt,
last_added_count: status.lastAddedCount,
schedules_added: status.totalAdded,
check_interval: checkInterval,
error_message: status.errorMessage,
enabled: bot.enabled,
});
}
return result;
});
/**
* POST /api/admin/bots/:id/start
* 시작
*/
fastify.post('/:id/start', {
schema: {
tags: ['admin/bots'],
summary: '봇 시작',
security: [{ bearerAuth: [] }],
},
preHandler: [fastify.authenticate],
}, async (request, reply) => {
const { id } = request.params;
try {
await scheduler.startBot(id);
return { success: true, message: '봇이 시작되었습니다.' };
} catch (err) {
return reply.code(400).send({ error: err.message });
}
});
/**
* POST /api/admin/bots/:id/stop
* 정지
*/
fastify.post('/:id/stop', {
schema: {
tags: ['admin/bots'],
summary: '봇 정지',
security: [{ bearerAuth: [] }],
},
preHandler: [fastify.authenticate],
}, async (request, reply) => {
const { id } = request.params;
try {
await scheduler.stopBot(id);
return { success: true, message: '봇이 정지되었습니다.' };
} catch (err) {
return reply.code(400).send({ error: err.message });
}
});
/**
* POST /api/admin/bots/:id/sync-all
* 전체 동기화
*/
fastify.post('/:id/sync-all', {
schema: {
tags: ['admin/bots'],
summary: '봇 전체 동기화',
security: [{ bearerAuth: [] }],
},
preHandler: [fastify.authenticate],
}, async (request, reply) => {
const { id } = request.params;
const bot = bots.find(b => b.id === id);
if (!bot) {
return reply.code(404).send({ error: '봇을 찾을 수 없습니다.' });
}
try {
let result;
if (bot.type === 'youtube') {
result = await fastify.youtubeBot.syncAllVideos(bot);
} else if (bot.type === 'x') {
result = await fastify.xBot.syncAllTweets(bot);
} else {
return reply.code(400).send({ error: '지원하지 않는 봇 타입입니다.' });
}
// 상태 업데이트
const status = await scheduler.getStatus(id);
await fastify.redis.set(`bot:status:${id}`, JSON.stringify({
...status,
lastCheckAt: new Date().toISOString(),
lastAddedCount: result.addedCount,
totalAdded: (status.totalAdded || 0) + result.addedCount,
updatedAt: new Date().toISOString(),
}));
return {
success: true,
addedCount: result.addedCount,
total: result.total,
};
} catch (err) {
fastify.log.error(`[${id}] 전체 동기화 오류:`, err);
return reply.code(500).send({ error: err.message });
}
});
/**
* GET /api/admin/quota-warning
* 할당량 경고 조회
*/
fastify.get('/quota-warning', {
schema: {
tags: ['admin/bots'],
summary: '할당량 경고 조회',
security: [{ bearerAuth: [] }],
},
preHandler: [fastify.authenticate],
}, async (request, reply) => {
const data = await redis.get(QUOTA_WARNING_KEY);
if (data) {
return { active: true, ...JSON.parse(data) };
}
return { active: false };
});
/**
* DELETE /api/admin/quota-warning
* 할당량 경고 해제
*/
fastify.delete('/quota-warning', {
schema: {
tags: ['admin/bots'],
summary: '할당량 경고 해제',
security: [{ bearerAuth: [] }],
},
preHandler: [fastify.authenticate],
}, async (request, reply) => {
await redis.del(QUOTA_WARNING_KEY);
return { success: true };
});
}