#!/usr/bin/env node /** * Reactivate Tang's daily scheduled tasks stuck in terminal `failed` state. * Dry-run by default; pass --apply to mutate rows. */ import process from 'node:process'; import mysql from 'mysql2/promise'; import { loadH5Environment } from './load-env.mjs'; import { nextDailyRunAt } from '../schedule-time.mjs'; loadH5Environment(import.meta.dirname); const TANG = 'a70ff537-8908-486e-9b6c-042e07cc25db'; const DAILY_TASK_IDS = [ '21799936-0532-420c-b144-65ea3846cde1', // 5:30 news '23456db0-918e-4055-9ca3-e17e83b2dc24', // 8:00 weather ]; const fmt = (ms) => new Date(Number(ms)).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai', hour12: false, }); async function main() { const apply = process.argv.includes('--apply'); const pool = mysql.createPool({ uri: process.env.DATABASE_URL, connectionLimit: 2 }); const now = Date.now(); const plan = []; for (const taskId of DAILY_TASK_IDS) { const [rows] = await pool.query( `SELECT id, title, status, recurrence, hour, minute, weekday, timezone, next_run_at, last_error, attempts FROM h5_scheduled_tasks WHERE id = ? AND user_id = ? LIMIT 1`, [taskId, TANG], ); const row = rows[0]; if (!row) { plan.push({ taskId, action: 'skip', reason: 'not_found' }); continue; } if (row.recurrence !== 'daily') { plan.push({ taskId, action: 'skip', reason: 'not_daily', title: row.title }); continue; } const nextRunAt = nextDailyRunAt({ hour: row.hour, minute: row.minute, timezone: row.timezone || 'Asia/Shanghai', now, }); plan.push({ taskId, title: row.title, fromStatus: row.status, fromNextRunAt: fmt(row.next_run_at), toStatus: 'active', toNextRunAt: fmt(nextRunAt), lastError: row.last_error, attempts: row.attempts, action: apply ? 'reactivated' : 'would_reactivate', }); if (apply) { await pool.query( `UPDATE h5_scheduled_tasks SET status = 'active', next_run_at = ?, locked_until = NULL, last_error = NULL, attempts = 0, updated_at = ? WHERE id = ? AND user_id = ?`, [nextRunAt, now, taskId, TANG], ); } } console.log(JSON.stringify({ apply, now: fmt(now), plan }, null, 2)); await pool.end(); } main().catch((err) => { console.error(err); process.exit(1); });