From ba3f3681ee1a3fe80944f70ee12820d414129ff3 Mon Sep 17 00:00:00 2001 From: shinya Date: Sun, 13 Jul 2025 22:27:17 +0800 Subject: [PATCH] feat: refine cron job --- package.json | 1 - pnpm-lock.yaml | 9 ---- .../api/cron/route.ts} | 38 +++++++++++++++-- src/app/layout.tsx | 1 - src/lib/cron.ts | 30 ------------- src/lib/d1.db.ts | 19 --------- start.js | 42 +++++++++++++++++++ 7 files changed, 76 insertions(+), 64 deletions(-) rename src/{lib/refreshRecordAndFavorites.ts => app/api/cron/route.ts} (86%) delete mode 100644 src/lib/cron.ts diff --git a/package.json b/package.json index 89402c2..69669ca 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,6 @@ "next": "^14.2.23", "next-pwa": "^5.6.0", "next-themes": "^0.4.6", - "node-cron": "^4.2.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-icons": "^5.4.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 77353cf..45f5795 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -62,9 +62,6 @@ importers: next-themes: specifier: ^0.4.6 version: 0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - node-cron: - specifier: ^4.2.0 - version: 4.2.0 react: specifier: ^18.2.0 version: 18.3.1 @@ -4774,10 +4771,6 @@ packages: no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} - node-cron@4.2.0: - resolution: {integrity: sha512-nOdP7uH7u55w7ybQq9fusXtsResok+ErzvOBydJUPBBaQ9W+EfBaBWFPgJ8sOB7FWQednDvVBJtgP5xA0bME7Q==} - engines: {node: '>=6.0.0'} - node-fetch@2.6.7: resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} engines: {node: 4.x || >=6.0.0} @@ -11689,8 +11682,6 @@ snapshots: lower-case: 2.0.2 tslib: 2.8.1 - node-cron@4.2.0: {} - node-fetch@2.6.7: dependencies: whatwg-url: 5.0.0 diff --git a/src/lib/refreshRecordAndFavorites.ts b/src/app/api/cron/route.ts similarity index 86% rename from src/lib/refreshRecordAndFavorites.ts rename to src/app/api/cron/route.ts index 5188b69..2b15103 100644 --- a/src/lib/refreshRecordAndFavorites.ts +++ b/src/app/api/cron/route.ts @@ -1,13 +1,45 @@ /* eslint-disable no-console */ +import { NextRequest, NextResponse } from 'next/server'; + import { db } from '@/lib/db'; import { fetchVideoDetail } from '@/lib/fetchVideoDetail'; import { SearchResult } from '@/lib/types'; -const STORAGE_TYPE = process.env.NEXT_PUBLIC_STORAGE_TYPE ?? 'localstorage'; +export const runtime = 'edge'; + +export async function GET(request: NextRequest) { + console.log(request.url); + try { + console.log('Cron job triggered:', new Date().toISOString()); + + refreshRecordAndFavorites(); + + return NextResponse.json({ + success: true, + message: 'Cron job executed successfully', + timestamp: new Date().toISOString(), + }); + } catch (error) { + console.error('Cron job failed:', error); + + return NextResponse.json( + { + success: false, + message: 'Cron job failed', + error: error instanceof Error ? error.message : 'Unknown error', + timestamp: new Date().toISOString(), + }, + { status: 500 } + ); + } +} async function refreshRecordAndFavorites() { - if (STORAGE_TYPE === 'localstorage') { + if ( + process.env.NEXT_PUBLIC_STORAGE_TYPE || + 'localstorage' === 'localstorage' + ) { return; } @@ -155,5 +187,3 @@ async function refreshRecordAndFavorites() { console.error('刷新播放记录/收藏任务启动失败', err); } } - -export default refreshRecordAndFavorites; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 9478beb..efd21f5 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,6 +1,5 @@ import type { Metadata, Viewport } from 'next'; import { Inter } from 'next/font/google'; -import '../lib/cron'; import './globals.css'; import 'sweetalert2/dist/sweetalert2.min.css'; diff --git a/src/lib/cron.ts b/src/lib/cron.ts deleted file mode 100644 index c413d0b..0000000 --- a/src/lib/cron.ts +++ /dev/null @@ -1,30 +0,0 @@ -import cron from 'node-cron'; - -import refreshRecordAndFavorites from '@/lib/refreshRecordAndFavorites'; - -/* - * 初始化定时任务:每个小时的 02 分执行一次。 - * 若需要添加更多任务,可在此文件中继续编写。 - */ - -declare global { - // 避免在开发热重载或多次导入时重复初始化 - // eslint-disable-next-line no-var - var __moonTVCronInitialized: boolean | undefined; -} - -if (!global.__moonTVCronInitialized) { - cron.schedule( - '2 * * * *', - async () => { - refreshRecordAndFavorites(); - }, - { - timezone: 'Asia/Shanghai', - } - ); - - global.__moonTVCronInitialized = true; -} - -export {}; // 仅用于确保这是一个模块 diff --git a/src/lib/d1.db.ts b/src/lib/d1.db.ts index 4a162b1..9f6bfdb 100644 --- a/src/lib/d1.db.ts +++ b/src/lib/d1.db.ts @@ -1,12 +1,8 @@ /* eslint-disable no-console, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ -import { getRequestContext } from '@cloudflare/next-on-pages'; - import { AdminConfig } from './admin.types'; import { Favorite, IStorage, PlayRecord } from './types'; -export const runtime = 'edge'; - // 搜索历史最大条数 const SEARCH_HISTORY_LIMIT = 20; @@ -120,17 +116,6 @@ const INIT_SQL = ` // 获取全局D1数据库实例 function getD1Database(): D1Database { - try { - // 在 Cloudflare Pages 环境中,通过 getRequestContext 访问 D1 数据库 - const { env } = getRequestContext(); - if (env && (env as any).DB) { - return (env as any).DB as D1Database; - } - } catch (error) { - // 如果 getRequestContext 失败,继续尝试其他方式 - console.warn('Failed to get request context:', error); - } - // 在 next-on-pages 环境中,D1 数据库可能通过 process.env 暴露 if (typeof process !== 'undefined' && (process.env as any).DB) { return (process.env as any).DB as D1Database; @@ -145,10 +130,6 @@ export class D1Storage implements IStorage { private db: D1Database | null = null; private initialized = false; - constructor() { - // 不在构造函数中初始化数据库,延迟到实际使用时 - } - private async getDatabase(): Promise { if (!this.db) { this.db = getD1Database(); diff --git a/start.js b/start.js index 593137e..c980cee 100644 --- a/start.js +++ b/start.js @@ -19,6 +19,14 @@ const intervalId = setInterval(() => { if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) { console.log('Server is up, stop polling.'); clearInterval(intervalId); + + // 服务器启动后,立即执行一次 cron 任务 + executeCronJob(); + + // 然后设置每小时执行一次 cron 任务 + setInterval(() => { + executeCronJob(); + }, 60 * 60 * 1000); // 每小时执行一次 } }); @@ -26,3 +34,37 @@ const intervalId = setInterval(() => { req.destroy(); }); }, 1000); + +// 执行 cron 任务的函数 +function executeCronJob() { + const cronUrl = `http://${process.env.HOSTNAME || 'localhost'}:${ + process.env.PORT || 3000 + }/api/cron`; + + console.log(`Executing cron job: ${cronUrl}`); + + const req = http.get(cronUrl, (res) => { + let data = ''; + + res.on('data', (chunk) => { + data += chunk; + }); + + res.on('end', () => { + if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) { + console.log('Cron job executed successfully:', data); + } else { + console.error('Cron job failed:', res.statusCode, data); + } + }); + }); + + req.on('error', (err) => { + console.error('Error executing cron job:', err); + }); + + req.setTimeout(30000, () => { + console.error('Cron job timeout'); + req.destroy(); + }); +}