feat(upgrades): enhance upgrade manager with detailed notifications for success and failure

This commit is contained in:
Sam Chau
2025-12-28 19:30:50 +10:30
parent 5b82b4305c
commit b7efaa567c
4 changed files with 142 additions and 2 deletions

View File

@@ -1,5 +1,6 @@
import { logger } from '$logger/logger.ts';
import { runUpgradeManager } from '../logic/upgradeManager.ts';
import { notificationManager } from '$notifications/NotificationManager.ts';
import type { JobDefinition, JobResult } from '../types.ts';
/**
@@ -52,6 +53,65 @@ export const upgradeManagerJob: JobDefinition = {
}
}
// Send notification summary (only if something was processed, excluding skipped)
const processedCount = result.successCount + result.failureCount;
if (processedCount > 0) {
const successfulInstances = result.instances.filter((i) => i.success);
const failedInstances = result.instances.filter((i) => !i.success && i.error && !i.error.includes('disabled') && !i.error.includes('not yet supported'));
const hasDryRun = result.instances.some((i) => i.dryRun);
// Build message lines for each successful instance
const messageLines: string[] = [];
for (const inst of successfulInstances) {
const dryRunLabel = inst.dryRun ? ' [DRY RUN]' : '';
messageLines.push(`**${inst.instanceName}: ${inst.filterName}${dryRunLabel}**`);
messageLines.push(`Filter: ${inst.matchedCount} matched → ${inst.afterCooldown} after cooldown`);
messageLines.push(`Selection: ${inst.itemsSearched}/${inst.itemsRequested} items`);
if (inst.items && inst.items.length > 0) {
messageLines.push(`Items: ${inst.items.join(', ')}`);
}
messageLines.push('');
}
// Add failed instances
for (const inst of failedInstances) {
messageLines.push(`**${inst.instanceName}: Failed**`);
messageLines.push(`Error: ${inst.error}`);
messageLines.push('');
}
let notificationType: string;
let title: string;
if (result.failureCount === 0) {
notificationType = 'upgrade.success';
title = hasDryRun ? 'Upgrade Completed (Dry Run)' : 'Upgrade Completed';
} else if (result.successCount === 0) {
notificationType = 'upgrade.failed';
title = 'Upgrade Failed';
} else {
notificationType = 'upgrade.partial';
title = 'Upgrade Partially Completed';
}
await notificationManager.notify({
type: notificationType,
title,
message: messageLines.join('\n').trim(),
metadata: {
successCount: result.successCount,
failureCount: result.failureCount,
dryRun: hasDryRun,
instances: result.instances.filter((i) => i.success).map((i) => ({
name: i.instanceName,
filter: i.filterName,
searched: i.itemsSearched,
items: i.items
}))
}
});
}
// Consider job failed only if all configs failed
if (result.failureCount > 0 && result.successCount === 0) {
return {
@@ -65,14 +125,23 @@ export const upgradeManagerJob: JobDefinition = {
output: message
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
await logger.error('Upgrade manager job failed', {
source: 'UpgradeManagerJob',
meta: { error: error instanceof Error ? error.message : String(error) }
meta: { error: errorMessage }
});
await notificationManager.notify({
type: 'upgrade.failed',
title: 'Upgrade Failed',
message: `Upgrade manager encountered an error: ${errorMessage}`,
metadata: { error: errorMessage }
});
return {
success: false,
error: error instanceof Error ? error.message : String(error)
error: errorMessage
};
}
}

View File

@@ -15,6 +15,11 @@ export interface UpgradeInstanceStatus {
success: boolean;
filterName?: string;
itemsSearched?: number;
itemsRequested?: number;
matchedCount?: number;
afterCooldown?: number;
items?: string[];
dryRun?: boolean;
error?: string;
}
@@ -79,6 +84,11 @@ async function processConfig(config: UpgradeConfig): Promise<UpgradeInstanceStat
success: log.status === 'success' || log.status === 'partial',
filterName: log.config.selectedFilter,
itemsSearched: log.selection.actualCount,
itemsRequested: log.selection.requestedCount,
matchedCount: log.filter.matchedCount,
afterCooldown: log.filter.afterCooldown,
items: log.selection.items.map((i) => i.title),
dryRun: config.dryRun,
error: log.status === 'failed' ? log.results.errors.join('; ') : undefined
};
} catch (error) {

View File

@@ -84,6 +84,26 @@ export const notificationTypes: NotificationType[] = [
label: 'Database Sync (Failed)',
category: 'Databases',
description: 'Notification when database sync fails'
},
// Upgrades
{
id: 'upgrade.success',
label: 'Upgrade Completed (Success)',
category: 'Upgrades',
description: 'Notification when all upgrade searches complete successfully'
},
{
id: 'upgrade.partial',
label: 'Upgrade Completed (Partial)',
category: 'Upgrades',
description: 'Notification when some upgrade searches succeed and some fail'
},
{
id: 'upgrade.failed',
label: 'Upgrade Failed',
category: 'Upgrades',
description: 'Notification when all upgrade searches fail'
}
];

View File

@@ -3,6 +3,7 @@ import type { Actions, ServerLoad } from '@sveltejs/kit';
import { arrInstancesQueries } from '$db/queries/arrInstances.ts';
import { upgradeConfigsQueries } from '$db/queries/upgradeConfigs.ts';
import { logger } from '$logger/logger.ts';
import { notificationManager } from '$notifications/NotificationManager.ts';
import type { FilterConfig, FilterMode } from '$lib/shared/filters.ts';
import { processUpgradeConfig } from '$lib/server/upgrades/processor.ts';
@@ -221,6 +222,31 @@ export const actions: Actions = {
upgradeConfigsQueries.incrementFilterIndex(id);
}
// Send notification
const isSuccess = result.status === 'success' || result.status === 'partial';
const dryRunLabel = result.config.dryRun ? ' [DRY RUN]' : '';
const itemsList = result.selection.items.map((i) => i.title).join(', ');
await notificationManager.notify({
type: isSuccess ? 'upgrade.success' : 'upgrade.failed',
title: `${instance.name}: ${result.config.selectedFilter}${dryRunLabel}`,
message: [
`Filter: ${result.filter.matchedCount} matched → ${result.filter.afterCooldown} after cooldown`,
`Selection: ${result.selection.actualCount}/${result.selection.requestedCount} items`,
`Results: ${result.results.searchesTriggered} searches, ${result.results.successful} successful`,
itemsList ? `Items: ${itemsList}` : null
].filter(Boolean).join('\n'),
metadata: {
instanceId: id,
instanceName: instance.name,
filterName: result.config.selectedFilter,
itemsSearched: result.selection.actualCount,
matchedCount: result.filter.matchedCount,
dryRun: result.config.dryRun,
items: result.selection.items.map((i) => i.title)
}
});
return {
success: true,
runResult: {
@@ -234,10 +260,25 @@ export const actions: Actions = {
}
};
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
await logger.error('Manual upgrade run failed', {
source: 'upgrades',
meta: { instanceId: id, error: err }
});
await notificationManager.notify({
type: 'upgrade.failed',
title: 'Upgrade Failed',
message: `${instance.name}: ${errorMessage}`,
metadata: {
instanceId: id,
instanceName: instance.name,
error: errorMessage,
dryRun: true
}
});
return fail(500, { error: 'Upgrade run failed. Check logs for details.' });
}
}