修复容器内 news/oil 定时失效: 运行时镜像携带 scripts/ + axios 转生产依赖 + cron 脚本模块惰性加载降级
Docker Build and Push / build-image (push) Failing after 9m34s

This commit is contained in:
jif2.zhang
2026-09-21 14:09:18 +08:00
parent 1d69915cf3
commit 7b4e59957b
4 changed files with 22 additions and 15 deletions
+1
View File
@@ -48,6 +48,7 @@ COPY --from=builder /app/server ./server
COPY --from=builder /app/generated ./generated COPY --from=builder /app/generated ./generated
COPY --from=builder /app/prisma ./prisma COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/prisma.config.ts ./prisma.config.ts COPY --from=builder /app/prisma.config.ts ./prisma.config.ts
COPY --from=builder /app/scripts ./scripts
COPY --from=builder /app/content ./content COPY --from=builder /app/content ./content
COPY --from=builder /app/public ./public COPY --from=builder /app/public ./public
+1 -10
View File
@@ -12,6 +12,7 @@
"@prisma/client": "^7.7.0", "@prisma/client": "^7.7.0",
"@zythum02/mijia-api": "^0.1.4", "@zythum02/mijia-api": "^0.1.4",
"antd-mobile": "^5.42.3", "antd-mobile": "^5.42.3",
"axios": "^1.15.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"express": "^4.21.0", "express": "^4.21.0",
@@ -32,7 +33,6 @@
"@tauri-apps/api": "^2.0.0", "@tauri-apps/api": "^2.0.0",
"@tauri-apps/cli": "^2.0.0", "@tauri-apps/cli": "^2.0.0",
"@vitejs/plugin-react": "^4.3.1", "@vitejs/plugin-react": "^4.3.1",
"axios": "^1.15.0",
"cheerio": "^1.2.0", "cheerio": "^1.2.0",
"concurrently": "^9.0.0", "concurrently": "^9.0.0",
"csv-parser": "^3.2.0", "csv-parser": "^3.2.0",
@@ -3152,7 +3152,6 @@
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/aws-ssl-profiles": { "node_modules/aws-ssl-profiles": {
@@ -3168,7 +3167,6 @@
"version": "1.15.0", "version": "1.15.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz",
"integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==", "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"follow-redirects": "^1.15.11", "follow-redirects": "^1.15.11",
@@ -3569,7 +3567,6 @@
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"delayed-stream": "~1.0.0" "delayed-stream": "~1.0.0"
@@ -3929,7 +3926,6 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=0.4.0" "node": ">=0.4.0"
@@ -4207,7 +4203,6 @@
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"es-errors": "^1.3.0", "es-errors": "^1.3.0",
@@ -4512,7 +4507,6 @@
"version": "1.15.11", "version": "1.15.11",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "individual", "type": "individual",
@@ -4549,7 +4543,6 @@
"version": "4.0.5", "version": "4.0.5",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"asynckit": "^0.4.0", "asynckit": "^0.4.0",
@@ -4764,7 +4757,6 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"has-symbols": "^1.0.3" "has-symbols": "^1.0.3"
@@ -5694,7 +5686,6 @@
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
"integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=10" "node": ">=10"
+1 -1
View File
@@ -34,6 +34,7 @@
"@prisma/client": "^7.7.0", "@prisma/client": "^7.7.0",
"@zythum02/mijia-api": "^0.1.4", "@zythum02/mijia-api": "^0.1.4",
"antd-mobile": "^5.42.3", "antd-mobile": "^5.42.3",
"axios": "^1.15.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"express": "^4.21.0", "express": "^4.21.0",
@@ -54,7 +55,6 @@
"@tauri-apps/api": "^2.0.0", "@tauri-apps/api": "^2.0.0",
"@tauri-apps/cli": "^2.0.0", "@tauri-apps/cli": "^2.0.0",
"@vitejs/plugin-react": "^4.3.1", "@vitejs/plugin-react": "^4.3.1",
"axios": "^1.15.0",
"cheerio": "^1.2.0", "cheerio": "^1.2.0",
"concurrently": "^9.0.0", "concurrently": "^9.0.0",
"csv-parser": "^3.2.0", "csv-parser": "^3.2.0",
+19 -4
View File
@@ -1,11 +1,25 @@
import cron from 'node-cron' import cron from 'node-cron'
import { prisma } from './db.js' import { prisma } from './db.js'
import mysql from 'mysql2/promise' import mysql from 'mysql2/promise'
import { fetchNewsList, persistNews } from '../scripts/fetchers/news.fetcher.js'
import { checkAfterRefresh } from './alert-engine.js' import { checkAfterRefresh } from './alert-engine.js'
import { insertCronAlert } from '../scripts/lib/cron-alert.js'
import { ApizeroError, oil, history } from './apizero/index.js' import { ApizeroError, oil, history } from './apizero/index.js'
// scripts/ 下的可选脚本模块 (部署容器可能缺失或依赖不全): 惰性加载,
// 单个 fetcher 缺失只降级对应任务, 不拖垮 cron 整体注册。
async function importNewsFetcher() {
const mod = await import('../scripts/fetchers/news.fetcher.js')
return { fetchNewsList: mod.fetchNewsList, persistNews: mod.persistNews }
}
async function alertError(taskName, message) {
try {
const { insertCronAlert } = await import('../scripts/lib/cron-alert.js')
await insertCronAlert(taskName, message)
} catch (e) {
console.error('[cron] 报警写入失败(忽略):', e.message)
}
}
function parseUrl(url) { function parseUrl(url) {
const u = new URL(url) const u = new URL(url)
return { host: u.hostname, port: Number(u.port) || 3306, user: decodeURIComponent(u.username), password: decodeURIComponent(u.password), database: u.pathname.replace(/^\//, '') } return { host: u.hostname, port: Number(u.port) || 3306, user: decodeURIComponent(u.username), password: decodeURIComponent(u.password), database: u.pathname.replace(/^\//, '') }
@@ -20,6 +34,7 @@ function scheduleNews() {
cron.schedule('0 6 * * *', async () => { cron.schedule('0 6 * * *', async () => {
console.log('[cron] 6:00 触发') console.log('[cron] 6:00 触发')
try { try {
const { fetchNewsList, persistNews } = await importNewsFetcher()
const list = await fetchNewsList() const list = await fetchNewsList()
const r = await persistNews(list) const r = await persistNews(list)
console.log('[cron] 新闻入库: 新增 ' + r.inserted + ' / 跳过 ' + r.skipped) console.log('[cron] 新闻入库: 新增 ' + r.inserted + ' / 跳过 ' + r.skipped)
@@ -30,7 +45,7 @@ function scheduleNews() {
} finally { await conn.end() } } finally { await conn.end() }
} catch (e) { } catch (e) {
console.error('[cron] 任务失败:', e.message) console.error('[cron] 任务失败:', e.message)
await insertCronAlert('新闻拉取+报警检测', e.message) await alertError('新闻拉取+报警检测', e.message)
} }
}, { timezone: 'Asia/Shanghai' }) }, { timezone: 'Asia/Shanghai' })
console.log('[cron] 已注册: 每天 06:00 拉取新闻 + 跨源报警检测') console.log('[cron] 已注册: 每天 06:00 拉取新闻 + 跨源报警检测')
@@ -69,7 +84,7 @@ function scheduleOilHistory() {
} else { } else {
console.error('[cron] 油价历史任务失败:', e.message) console.error('[cron] 油价历史任务失败:', e.message)
} }
await insertCronAlert('浙江油价历史抓取', e.message) await alertError('浙江油价历史抓取', e.message)
} }
}, { timezone: 'Asia/Shanghai' }) }, { timezone: 'Asia/Shanghai' })
console.log('[cron] 已注册: 每天 06:10 浙江油价历史抓取') console.log('[cron] 已注册: 每天 06:10 浙江油价历史抓取')