mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-06 00:47:52 +00:00
6b8c369745
* fix(notifications): stop Sencho version notifications from silently skipping Three independent defects combined to make version-update notifications silently fail even while Fleet overview correctly surfaced an update button: - The in-memory 6-hour cooldown was advanced before the network fetch, so a single transient failure at boot could lock the check for the rest of the container lifetime. Moved the cooldown update inside the success branch so failures retry on the next eval cycle. - MonitorService called the raw version fetch directly, bypassing the CacheService wrapper (TTL, inflight dedup, stale-on-error) that Fleet uses, so the two paths could diverge. Unified both on a shared getLatestVersion() helper in utils/version-check.ts. - The dedup key could carry stale state from a previous build and never self-clear. It now self-heals when the running version reaches the previously-notified version, so future releases re-fire as expected. Added diagnostic logs gated on debug mode for each skip branch, plus three regression tests covering cooldown-on-failure, cooldown-on-success, and dedup self-heal. * docs(notifications): drop legacy-upgrade framing from alerts troubleshooting Sencho has not shipped publicly, so troubleshooting entries written in 'this used to happen but now does Y' mode reference a past that does not exist for any reader. Rewrote the version-notification, image-update, and crash-alert troubleshooting entries to describe current behavior positively without referring to prior builds, upgrade paths, or legacy fixes. * chore(security): accept CVE-2026-33810 in bundled Docker CLI 29.4.0 Trivy now flags CVE-2026-33810 (Go stdlib crypto/x509 DNS constraint bypass, fixed in Go 1.26.2) in the Docker CLI static binary we ship. Docker CLI 29.4.0 is the latest upstream release and still links Go 1.26.1; no newer static binary exists yet. Same exposure profile as the already-accepted CVE-2026-32280: the Docker CLI and compose plugin only validate certificates from well-known registry CAs and the local Docker socket, not from attacker-controlled CAs with crafted DNS name constraints. Revisit on the next Docker CLI release that rebuilds against Go 1.26.2 or later.
77 lines
2.7 KiB
TypeScript
77 lines
2.7 KiB
TypeScript
import semver from 'semver';
|
|
import { CacheService } from '../services/CacheService';
|
|
|
|
/**
|
|
* Fetches the latest Sencho release version from GitHub or Docker Hub.
|
|
* Extracted from index.ts so both the fleet endpoint and MonitorService
|
|
* can share the same lookup logic.
|
|
*/
|
|
|
|
async function fetchFromGitHub(): Promise<string | null> {
|
|
const res = await fetch('https://api.github.com/repos/AnsoCode/Sencho/releases/latest', {
|
|
headers: { Accept: 'application/vnd.github+json', 'User-Agent': 'Sencho' },
|
|
signal: AbortSignal.timeout(10000),
|
|
});
|
|
if (!res.ok) return null;
|
|
const data = await res.json() as { tag_name?: string };
|
|
const tag = data.tag_name?.replace(/^v/, '') ?? null;
|
|
return tag && semver.valid(tag) ? tag : null;
|
|
}
|
|
|
|
async function fetchFromDockerHub(): Promise<string | null> {
|
|
const res = await fetch(
|
|
'https://hub.docker.com/v2/repositories/saelix/sencho/tags/?page_size=50&ordering=last_updated',
|
|
{ headers: { 'User-Agent': 'Sencho' }, signal: AbortSignal.timeout(10000) },
|
|
);
|
|
if (!res.ok) return null;
|
|
const data = await res.json() as { results?: { name: string }[] };
|
|
const tags = (data.results ?? [])
|
|
.map(t => t.name)
|
|
.filter(n => semver.valid(n));
|
|
if (tags.length === 0) return null;
|
|
tags.sort(semver.rcompare);
|
|
return tags[0];
|
|
}
|
|
|
|
export async function fetchLatestSenchoVersion(): Promise<string> {
|
|
try {
|
|
const gh = await fetchFromGitHub();
|
|
if (gh) return gh;
|
|
} catch (err) {
|
|
// GitHub API fails for private repos or rate limits; try Docker Hub
|
|
console.warn('[VersionCheck] GitHub fetch failed:', (err as Error).message);
|
|
}
|
|
try {
|
|
const hub = await fetchFromDockerHub();
|
|
if (hub) return hub;
|
|
} catch (err) {
|
|
console.warn('[VersionCheck] Docker Hub fetch failed:', (err as Error).message);
|
|
}
|
|
// Throw so CacheService falls back to a stale value if one exists,
|
|
// and so we do not poison the cache with null.
|
|
throw new Error('Both GitHub and Docker Hub version lookups failed');
|
|
}
|
|
|
|
/**
|
|
* Cached wrapper shared by the Fleet endpoint and MonitorService.
|
|
* CacheService provides TTL, inflight deduplication, and stale-on-error
|
|
* fallback so transient network blips do not cause user-visible gaps.
|
|
*/
|
|
const LATEST_VERSION_CACHE_KEY = 'latest-version';
|
|
const LATEST_VERSION_CACHE_TTL = 30 * 60 * 1000; // 30 minutes
|
|
|
|
export async function getLatestVersion(forceRefresh = false): Promise<string | null> {
|
|
if (forceRefresh) {
|
|
CacheService.getInstance().invalidate(LATEST_VERSION_CACHE_KEY);
|
|
}
|
|
try {
|
|
return await CacheService.getInstance().getOrFetch<string>(
|
|
LATEST_VERSION_CACHE_KEY,
|
|
LATEST_VERSION_CACHE_TTL,
|
|
fetchLatestSenchoVersion,
|
|
);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|