mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-07 09:24:09 +00:00
fix(notifications): resolve version notification showing 0.0.0 and backfill missing image update notifications (#586)
- Read running Sencho version from the packaged manifest via getSenchoVersion() instead of process.env.npm_package_version, which is undefined when launched via node dist/index.js (production Docker). Skip the update check entirely when the version cannot be resolved. - Add a one-time backfill pass in ImageUpdateService so users who upgraded to a Sencho version with the notification pipeline receive a catch-up entry for stacks already flagged as having updates before the upgrade. - Surface dispatch failures as error-level entries in the in-app notification bell via a direct notification_history write, so misconfigured webhooks are visible without tailing logs. - Extend test coverage for both paths: mock getSenchoVersion (including the null-version case) and add dispatch-path tests for transition, no-re-fire, backfill, and error surfacing. - Expand alerts-notifications docs with per-instance 6-hour check cadence, an example version message, a backfill note, and three new troubleshooting entries.
This commit is contained in:
@@ -9,6 +9,9 @@ import { RegistryService } from './RegistryService';
|
||||
import { NodeRegistry } from './NodeRegistry';
|
||||
import { NotificationService } from './NotificationService';
|
||||
import { isDebugEnabled } from '../utils/debug';
|
||||
import { getErrorMessage } from '../utils/errors';
|
||||
|
||||
const BACKFILL_KEY = 'image_update_notifications_backfilled';
|
||||
|
||||
// ─── Image ref parsing ────────────────────────────────────────────────────────
|
||||
|
||||
@@ -377,6 +380,10 @@ export class ImageUpdateService {
|
||||
// Read previous state to detect new updates for notifications
|
||||
const previousState = db.getStackUpdateStatus(nodeId);
|
||||
|
||||
// One-time backfill: pre-existing has_update rows predate the notification pipeline;
|
||||
// treat them as unnotified on first run so users get a catch-up entry per affected stack.
|
||||
const isBackfilled = db.getSystemState(BACKFILL_KEY) === '1';
|
||||
|
||||
// Write status for ALL stacks (including those with no pullable images)
|
||||
const now = Date.now();
|
||||
let updatesFound = 0;
|
||||
@@ -386,7 +393,7 @@ export class ImageUpdateService {
|
||||
if (hasUpdate) {
|
||||
updatesFound++;
|
||||
// Notify only on state transition: was false/absent, now true
|
||||
if (!previousState[stackName]) {
|
||||
if (!isBackfilled || !previousState[stackName]) {
|
||||
newlyUpdated.push(stackName);
|
||||
}
|
||||
}
|
||||
@@ -405,10 +412,25 @@ export class ImageUpdateService {
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(`[ImageUpdateService] Failed to dispatch update notification for "${stackName}":`, e);
|
||||
// Direct DB write to avoid recursing through dispatchAlert if it is what failed.
|
||||
try {
|
||||
db.addNotificationHistory({
|
||||
level: 'error',
|
||||
message: `[Node: ${nodeName}] Failed to notify about image updates for stack "${stackName}": ${getErrorMessage(e, String(e))}`,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
} catch (dbErr) {
|
||||
console.error('[ImageUpdateService] Failed to record dispatch error:', dbErr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the backfill flag after the first run so future checks use strict transitions.
|
||||
if (!isBackfilled) {
|
||||
db.setSystemState(BACKFILL_KEY, '1');
|
||||
}
|
||||
|
||||
console.log(`[ImageUpdateService] Node ${nodeId}: checked ${allImages.size} image(s), ${updatesFound} stack(s) with updates`);
|
||||
|
||||
// Prune stale entries for stacks no longer on disk (reuse previousState to avoid extra DB read)
|
||||
|
||||
@@ -5,7 +5,7 @@ import semver from 'semver';
|
||||
import DockerController from './DockerController';
|
||||
import { DatabaseService } from './DatabaseService';
|
||||
import { NotificationService } from './NotificationService';
|
||||
import { isValidVersion } from './CapabilityRegistry';
|
||||
import { isValidVersion, getSenchoVersion } from './CapabilityRegistry';
|
||||
import { fetchLatestSenchoVersion } from '../utils/version-check';
|
||||
import { isDebugEnabled } from '../utils/debug';
|
||||
|
||||
@@ -266,7 +266,9 @@ export class MonitorService {
|
||||
if (Date.now() - this.lastVersionCheckAt > MonitorService.VERSION_CHECK_INTERVAL_MS) {
|
||||
this.lastVersionCheckAt = Date.now();
|
||||
try {
|
||||
const currentVersion = process.env.npm_package_version || '0.0.0';
|
||||
// Resolve from the packaged manifest: process.env.npm_package_version is
|
||||
// only set by npm scripts, so it is undefined in Docker (node dist/index.js).
|
||||
const currentVersion = getSenchoVersion();
|
||||
const latest = await fetchLatestSenchoVersion();
|
||||
if (isValidVersion(latest) && isValidVersion(currentVersion) && semver.gt(latest, currentVersion)) {
|
||||
const db = DatabaseService.getInstance();
|
||||
@@ -278,6 +280,8 @@ export class MonitorService {
|
||||
`Sencho ${latest} is available (currently running ${currentVersion}). Visit the Fleet dashboard to update.`);
|
||||
db.setSystemState(stateKey, latest);
|
||||
}
|
||||
} else if (isDebugEnabled() && !isValidVersion(currentVersion)) {
|
||||
console.debug('[Monitor:diag] Sencho version unresolvable; skipping update notification');
|
||||
}
|
||||
} catch (e) {
|
||||
// Network errors are expected; do not spam logs
|
||||
|
||||
Reference in New Issue
Block a user