Fix player sync empty overwrites and force CSS cache refresh.
Keep D1 recent/heroes/peers/rank when OpenDota is partial; bump site to 0.6.55 and shorten style.css cache so「我」layout is not stuck on stale CSS. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -37,8 +37,8 @@ export async function upsertProfile(db, accountId, profile) {
|
||||
availability_complete, source, fetched_at, enriched_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(account_id) DO UPDATE SET
|
||||
rank_tier=excluded.rank_tier,
|
||||
leaderboard_rank=excluded.leaderboard_rank,
|
||||
rank_tier=COALESCE(excluded.rank_tier, player_profiles.rank_tier),
|
||||
leaderboard_rank=COALESCE(excluded.leaderboard_rank, player_profiles.leaderboard_rank),
|
||||
availability_status=excluded.availability_status,
|
||||
availability_note=excluded.availability_note,
|
||||
availability_complete=excluded.availability_complete,
|
||||
@@ -102,9 +102,11 @@ export async function upsertStats(db, accountId, scope, stats) {
|
||||
}
|
||||
|
||||
export async function replaceHeroes(db, accountId, heroes) {
|
||||
// Empty OpenDota heroes must not wipe a good cache.
|
||||
if (!Array.isArray(heroes) || heroes.length === 0) return;
|
||||
const now = utcNow();
|
||||
await db.prepare(`DELETE FROM player_heroes WHERE account_id = ?`).bind(accountId).run();
|
||||
for (const h of heroes || []) {
|
||||
for (const h of heroes) {
|
||||
await db
|
||||
.prepare(
|
||||
`INSERT INTO player_heroes (
|
||||
@@ -127,9 +129,11 @@ export async function replaceHeroes(db, accountId, heroes) {
|
||||
}
|
||||
|
||||
export async function replacePeers(db, accountId, peers) {
|
||||
// Empty OpenDota peers must not wipe a good cache.
|
||||
if (!Array.isArray(peers) || peers.length === 0) return;
|
||||
const now = utcNow();
|
||||
await db.prepare(`DELETE FROM player_peers WHERE account_id = ?`).bind(accountId).run();
|
||||
for (const p of peers || []) {
|
||||
for (const p of peers) {
|
||||
await db
|
||||
.prepare(
|
||||
`INSERT INTO player_peers (
|
||||
|
||||
@@ -193,9 +193,17 @@ async function syncAccount(env, msg) {
|
||||
};
|
||||
}
|
||||
|
||||
const existing = await loadPlayerBundle(env.DB, accountId);
|
||||
const existingRecentN = Array.isArray(existing && existing.recent)
|
||||
? existing.recent.length
|
||||
: 0;
|
||||
const existingR20 =
|
||||
existing && existing.recent_20 ? Number(existing.recent_20.sample) || 0 : 0;
|
||||
const odRecentOk = Array.isArray(recentRaw) && recentRaw.length > 0;
|
||||
|
||||
const fetched = utcNow();
|
||||
const availability = mergeAvailability({
|
||||
opendotaRecentN: Array.isArray(recentRaw) ? recentRaw.length : 0,
|
||||
opendotaRecentN: odRecentOk ? recentRaw.length : 0,
|
||||
career,
|
||||
steamHistoryStatus: steamStatus,
|
||||
fetchedAt: fetched,
|
||||
@@ -204,11 +212,13 @@ async function syncAccount(env, msg) {
|
||||
availability.status = "private";
|
||||
availability.note = "未公开比赛数据";
|
||||
}
|
||||
// Steam public but OpenDota empty → almost always rate-limit; retry queue.
|
||||
// Cold account + Steam public + empty OpenDota → rate-limit; retry queue.
|
||||
if (
|
||||
availability.status === "syncing" &&
|
||||
(!Array.isArray(recentRaw) || recentRaw.length === 0) &&
|
||||
!career
|
||||
!odRecentOk &&
|
||||
!career &&
|
||||
existingRecentN === 0 &&
|
||||
!(existing && existing.career && existing.career.games > 0) &&
|
||||
steamStatus === 1
|
||||
) {
|
||||
throw new Error("OpenDota empty while Steam public — retry");
|
||||
}
|
||||
@@ -222,14 +232,28 @@ async function syncAccount(env, msg) {
|
||||
availability,
|
||||
enriched_at: fetched,
|
||||
};
|
||||
// Partial OpenDota (career ok, recent empty): keep prior freshness so /me
|
||||
// stays soft-stale and do not pretend the row is fully refreshed.
|
||||
if (!odRecentOk && (existingR20 > 0 || existingRecentN > 0)) {
|
||||
profile.enriched_at =
|
||||
(existing && existing.enriched_at) || profile.enriched_at;
|
||||
if (existing && existing.availability && existing.availability.fetched_at) {
|
||||
availability.fetched_at = existing.availability.fetched_at;
|
||||
}
|
||||
}
|
||||
|
||||
await upsertProfile(env.DB, accountId, profile);
|
||||
if (career) await upsertStats(env.DB, accountId, "career", career);
|
||||
await upsertStats(env.DB, accountId, "recent20", aggregateFromRows(recent20, 20));
|
||||
const recentAgg = aggregateFromRows(recent20, 20);
|
||||
if ((recentAgg.sample || 0) > 0) {
|
||||
await upsertStats(env.DB, accountId, "recent20", recentAgg);
|
||||
await upsertMatches(env.DB, accountId, recent20);
|
||||
} else if (existingR20 <= 0 && existingRecentN <= 0) {
|
||||
await upsertStats(env.DB, accountId, "recent20", recentAgg);
|
||||
}
|
||||
if (activity180) await upsertStats(env.DB, accountId, "recent180", activity180);
|
||||
await replaceHeroes(env.DB, accountId, topHeroes);
|
||||
await replacePeers(env.DB, accountId, peerRows);
|
||||
await upsertMatches(env.DB, accountId, recent20);
|
||||
|
||||
// Optional: store a published match detail into R2 (deduped by match_id).
|
||||
if (msg.kind === "publish_match" && msg.match_id && env.MATCHES) {
|
||||
|
||||
Reference in New Issue
Block a user