@@ -153,12 +153,53 @@ async function main() {
153153 const aggregated = await fetchAggregatedFromBackend ( ) ;
154154
155155 if ( aggregated === null ) {
156+ // 拉不到后端时优先保留 generated/site-leaderboard.json 旧版本:
157+ // 一次 fetch 失败(CF 临时挑战 / Vercel runner IP 信誉低 / 后端短暂抖动)
158+ // 不应该把 commit 进 git 的好数据冲成空数组上线。
159+ //
160+ // 三种情况:
161+ // 1. 文件已存在 + 内容是非空数组 → 保留旧数据 exit 0
162+ // 2. 文件已存在但是空数组 / 不是数组 → 维持原状 exit 0(不主动覆盖)
163+ // 3. 文件不存在(首次 build / 干净 cache)→ 写空数组兜底 exit 0
164+ let preservedExisting = false ;
165+ try {
166+ const existing = await fs . readFile ( outputAbs , "utf-8" ) ;
167+ try {
168+ const parsed = JSON . parse ( existing ) ;
169+ if ( Array . isArray ( parsed ) && parsed . length > 0 ) {
170+ console . warn (
171+ `[generate-leaderboard] 后端不可用,但保留 ${ OUTPUT } 已有 ${ parsed . length } 条数据,不覆盖。 | Backend unreachable; keeping existing leaderboard with ${ parsed . length } entries.` ,
172+ ) ;
173+ } else {
174+ console . warn (
175+ `[generate-leaderboard] 后端不可用,且 ${ OUTPUT } 已有内容非有效非空数组,维持原状。` ,
176+ ) ;
177+ }
178+ preservedExisting = true ;
179+ } catch {
180+ // 文件存在但 JSON 损坏:当作没有,走下面写空兜底
181+ console . warn (
182+ `[generate-leaderboard] ${ OUTPUT } 已存在但 JSON 解析失败,按"首次 build"兜底覆盖空数组。` ,
183+ ) ;
184+ }
185+ } catch ( readErr ) {
186+ // ENOENT 等:文件不存在,走兜底
187+ if ( readErr && readErr . code !== "ENOENT" ) {
188+ console . warn (
189+ "[generate-leaderboard] 读取既有 leaderboard 失败:" ,
190+ readErr instanceof Error ? readErr . message : readErr ,
191+ ) ;
192+ }
193+ }
194+
195+ if ( preservedExisting ) {
196+ process . exit ( 0 ) ;
197+ }
198+
199+ // 文件不存在 / 损坏:写空兜底,避免后续 Next import 抛 ENOENT
156200 console . error (
157- "[generate-leaderboard] 后端不可用 ,写入空榜单以放行构建。 | Backend unreachable, writing empty leaderboard to unblock build." ,
201+ "[generate-leaderboard] 后端不可用且本地无可保留数据 ,写入空榜单以放行构建。 | Backend unreachable and no existing data , writing empty leaderboard to unblock build." ,
158202 ) ;
159- // mkdir + writeFile 必须放同一个 try:任一步失败都意味着 generated/site-leaderboard.json
160- // 不存在,后续 Next 端 import 会抛更难定位的 ENOENT。这种情况 build 必须 fail-fast,
161- // 不能 exit 0 让"看起来一切正常"的 deploy 把站点搞挂。
162203 try {
163204 await ensureParentDir ( outputAbs ) ;
164205 await fs . writeFile ( outputAbs , "[]" , "utf-8" ) ;
0 commit comments