diff --git a/backend/src/plugins/scheduler.js b/backend/src/plugins/scheduler.js index 96e01cd..3a03e2e 100644 --- a/backend/src/plugins/scheduler.js +++ b/backend/src/plugins/scheduler.js @@ -20,6 +20,19 @@ function isActiveMonth(bot) { return months.includes(monthKST()); } +/** + * DB JSON 컬럼 안전 파싱. 깨진 값 하나가 getAllBots 전체를 막지 않도록 fallback 반환. + */ +function safeParse(value, fallback = null) { + if (value == null) return fallback; + if (typeof value !== 'string') return value; + try { + return JSON.parse(value); + } catch { + return fallback; + } +} + /** * 에러 객체에서 활동 로그용 details 구성 * - err.cause(Node fetch failed의 진짜 원인 등), err.code를 함께 포함 @@ -47,11 +60,7 @@ async function schedulerPlugin(fastify, opts) { 'SELECT * FROM bot_youtube' ); return rows.map(row => { - const weekly = row.weekly_schedule_config - ? (typeof row.weekly_schedule_config === 'string' - ? JSON.parse(row.weekly_schedule_config) - : row.weekly_schedule_config) - : null; + const weekly = safeParse(row.weekly_schedule_config, null); // weekly 모드면 시작 시각에만 트리거, 아니면 cron_interval 분 주기 let cronExpr; @@ -72,23 +81,11 @@ async function schedulerPlugin(fastify, opts) { bannerUrl: row.banner_url, cron: cronExpr, enabled: row.enabled === 1, - titleFilters: row.title_filters - ? (typeof row.title_filters === 'string' - ? JSON.parse(row.title_filters) - : row.title_filters) - : [], - defaultMemberIds: row.default_member_ids - ? (typeof row.default_member_ids === 'string' - ? JSON.parse(row.default_member_ids) - : row.default_member_ids) - : [], + titleFilters: safeParse(row.title_filters, []), + defaultMemberIds: safeParse(row.default_member_ids, []), extractMembersFromDesc: row.extract_members_from_desc === 1, extractMembersFromTitle: row.extract_members_from_title === 1, - autoScheduleNext: row.auto_schedule_config - ? (typeof row.auto_schedule_config === 'string' - ? JSON.parse(row.auto_schedule_config) - : row.auto_schedule_config) - : null, + autoScheduleNext: safeParse(row.auto_schedule_config, null), weeklySchedule: weekly, }; }); @@ -111,11 +108,7 @@ async function schedulerPlugin(fastify, opts) { nitterUrl: process.env.NITTER_URL || 'http://nitter:8080', cron: `*/${row.cron_interval} * * * *`, enabled: row.enabled === 1, - textFilters: row.text_filters - ? (typeof row.text_filters === 'string' - ? JSON.parse(row.text_filters) - : row.text_filters) - : [], + textFilters: safeParse(row.text_filters, []), includeRetweets: row.include_retweets === 1, extractYoutube: row.extract_youtube === 1, excludeManagedChannels: row.exclude_managed_channels === 1,