container update notifications now fire properly

This commit is contained in:
jarek committed 2026-08-18 10:05:37 +02:00
1 parent 6fe735ad97
commit f7ca18fa0c
3 files changed
+51 -19

No files matched your search

@@ -0,0 +1,32 @@
/**
* Pure notification-event decision logic - NO db/docker imports, so it unit-tests
* under the bun runner. index.ts re-exports and uses these; keeping the decisions here
* means the full "which events fire for which input" matrix is testable in isolation.
*
* `NotificationEventType` is a type-only import (erased at build), so it doesn't pull
* better-sqlite3 in through db.ts.
*/
import type { NotificationEventType } from '../db';
/**
* Map a raw Docker event action to the notification event type it should raise, or null
* when the action isn't one we notify on. Docker has no "updated" action - a container
* update is a die/kill of the old container followed by start of the new one, so those
* map to container_exited/container_started here. The "container was updated" signal is
* raised separately (see updateEventTypes) from the recreate flow, not from a raw event.
*/
export function mapActionToEventType(action: string): NotificationEventType | null {
const mapping: Record<string, NotificationEventType> = {
start: 'container_started',
stop: 'container_stopped',
restart: 'container_restarted',
die: 'container_exited',
kill: 'container_exited',
oom: 'container_oom',
'health_status: unhealthy': 'container_unhealthy',
'health_status: healthy': 'container_healthy',
pull: 'image_pulled'
};
return mapping[action] || null;
}
+4 -19
View File
@@ -26,6 +26,9 @@ import {
import type { NotificationPayload, NotificationResult } from './shared';
export type { NotificationPayload, NotificationResult } from './shared';
// The pure docker-action -> event-type map lives in events-core (unit-testable).
import { mapActionToEventType } from './events-core';
export { mapActionToEventType } from './events-core';
import { sendSmtpNotification } from './smtp';
import { sendDiscord } from './discord';
@@ -166,21 +169,6 @@ export async function testNotification(setting: NotificationSettingData): Promis
return { success: false, error: 'Unknown notification type' };
}
// Map Docker action to notification event type
function mapActionToEventType(action: string): NotificationEventType | null {
const mapping: Record<string, NotificationEventType> = {
'start': 'container_started',
'stop': 'container_stopped',
'restart': 'container_restarted',
'die': 'container_exited',
'kill': 'container_exited',
'oom': 'container_oom',
'health_status: unhealthy': 'container_unhealthy',
'health_status: healthy': 'container_healthy',
'pull': 'image_pulled'
};
return mapping[action] || null;
}
// Scanner image patterns to exclude from notifications
const SCANNER_IMAGE_PATTERNS = [
@@ -257,10 +245,7 @@ export async function sendEventNotification(
payload: NotificationPayload,
environmentId?: number
): Promise<{ success: boolean; sent: number }> {
let enrichedPayload: NotificationPayload = {
...payload,
eventType
};
let enrichedPayload: NotificationPayload = { ...payload, eventType };
if (environmentId) {
const env = await getEnvironment(environmentId);
if (env) {
@@ -693,6 +693,21 @@ export async function recreateContainer(
} catch (e: any) {
log?.(`WARNING: dependent-child reconnect step errored (parent update stands): ${e?.message}`);
}
// recreateContainer is only ever called to UPDATE a container's image (scheduled
// auto-update AND the manual batch-update UI), so a successful recreate is a
// container_updated event. This is what the UI's "container updated" toggle
// promises; auto_update_success is raised separately by the scheduler path. (#1424)
try {
await sendEventNotification('container_updated', {
title: 'Container updated',
message: `Container "${containerName}" was updated to a new image (${imageName})`,
type: 'success'
}, envId);
} catch (e: any) {
log?.(`WARNING: container_updated notification failed (update stands): ${e?.message}`);
}
return { success: true };
} catch (error: any) {
log?.(`Failed to recreate container: ${error.message}`);