mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-22 08:06:42 +00:00
fix(alerts): harden with security fixes, design compliance, and test coverage (#570)
* fix(alerts): harden with security fixes, design compliance, and test coverage Add authMiddleware to all alert endpoints, validate notification test dispatch inputs, fix restart_count metric via Docker inspect, correct network metric units, replace any types with DockerContainerStats interface, add webhook timeouts and dispatch error tracking. Frontend: migrate Select to Combobox, add ScrollArea and delete confirmation AlertDialog, fix icon strokeWidth to 1.5. Add update availability notifications for both Sencho version updates (6-hour check in MonitorService) and stack image updates (state transition detection in ImageUpdateService). Extract shared version fetch logic into utils/version-check.ts. Add diagnostic logging gated behind developer_mode for MonitorService breach state machine and NotificationService dispatch routing. Tests: 24 new alert API integration tests, restart_count and version check unit tests (688 total passing). Docs updated with HTTPS requirement, update notifications section, and troubleshooting guide. * fix(alerts): remove unused TEST_USERNAME import in alerts-api tests
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
import semver from 'semver';
|
||||
|
||||
/**
|
||||
* 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');
|
||||
}
|
||||
Reference in New Issue
Block a user