Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/lib/update-check.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,29 @@
import "server-only"
import { after } from "next/server"
import { APP_VERSION, REPO_URL } from "./version"
import { getSetting, setSetting, type SettingValue } from "./settings"

// The daily cron (see src/lib/jobs/index.ts) already refreshes this, so a
// page load normally just reads the cached setting below — no outbound
// request. This is only a backstop for when the cron missed its run (e.g.
// the server was down at 06:00): if the cache is older than this, a page
// load triggers one background refresh after the response is sent.
const STALE_AFTER_MS = 20 * 60 * 60 * 1000

// Dedupes concurrent triggers within this process (e.g. several tabs/admins
// loading a page at once while the cache is stale).
let refreshInFlight: Promise<void> | null = null

function triggerBackgroundRefresh() {
if (refreshInFlight) return
refreshInFlight = checkForUpdate()
.then(() => undefined)
.catch((error) => console.error("[update-check]", error))
.finally(() => {
refreshInFlight = null
})
}

const [REPO_OWNER, REPO_NAME] = new URL(REPO_URL).pathname.slice(1).split("/")

/** Parses "1.2.3" into comparable numeric parts. Non-numeric parts sort as 0. */
Expand Down Expand Up @@ -55,6 +77,10 @@ export async function getUpdateStatus(): Promise<{
updateAvailable: boolean
}> {
const latest = await getSetting("system.updateCheck")

const stale = !latest || Date.now() - new Date(latest.checkedAt).getTime() > STALE_AFTER_MS
if (stale) after(triggerBackgroundRefresh)

return {
current: APP_VERSION,
latest,
Expand Down
Loading