Add extensible notification providers + UI/API

Introduce a provider-based notification system and wire it through the API and admin UI. Added INotificationProvider + notification service implementation and providers (apprise, discord, ntfy, pushover), plus a GET /api/admin/notifications/providers endpoint to expose provider metadata. Refactored code to use provider type strings (removed enum coupling), updated masking/encryption calls, and simplified the test notification endpoint to accept backendId or type+config and call sendToBackend directly.

UI: NotificationsTab now fetches provider metadata and renders provider cards and dynamic config forms (fields driven by provider metadata). Added config field rendering, improved backend cards, and edit/delete actions.

APIs: New providers route, updated admin notification CRUD routes to validate provider types dynamically, updated test route schema. Added download-client categories POST API to fetch categories from clients and wired postImportCategory handling in download-client routes.

Other notable changes: BookDate now fetches Claude models dynamically from Anthropic's Models API; added paginated model fetch helper. Added ALLOW_WEAK_PASSWORD flag exposure to auth providers and password change logic. Doc updates and various tests added/updated. File-organization doc clarifies EPERM fix using stream-based copy.
This commit is contained in:
kikootwo
2026-02-10 15:06:20 -05:00
parent 4a38dd3da8
commit af0eaceb98
73 changed files with 3421 additions and 866 deletions
@@ -180,6 +180,9 @@ export async function processOrganizeFiles(payload: OrganizeFilesPayload): Promi
},
});
// Apply post-import category to torrent client if configured
await applyPostImportCategory(requestId, logger);
logger.info(`Request ${requestId} completed successfully - status: downloaded`, {
success: true,
message: 'Files organized successfully',
@@ -606,6 +609,9 @@ async function processEbookOrganization(
},
});
// Apply post-import category to torrent client if configured
await applyPostImportCategory(requestId, logger);
logger.info(`Ebook request ${requestId} completed - status: downloaded (terminal)`);
// Send "available" notification for ebooks at downloaded state
@@ -753,6 +759,59 @@ async function createEbookRequestIfEnabled(
}
}
// =========================================================================
// POST-IMPORT CATEGORY
// =========================================================================
/**
* Apply post-import category to the download client after successful import.
* Only applies to torrent clients (qBittorrent/Transmission) when configured.
* Non-fatal: logs a warning on failure but does not fail the job.
*/
async function applyPostImportCategory(
requestId: string,
logger: RMABLogger
): Promise<void> {
try {
// Get download history to find client type and download ID
const downloadHistory = await prisma.downloadHistory.findFirst({
where: { requestId },
orderBy: { createdAt: 'desc' },
});
if (!downloadHistory?.downloadClientId || !downloadHistory?.downloadClient) {
return;
}
const clientType = downloadHistory.downloadClient as DownloadClientType;
// Only applies to torrent clients
const protocol = CLIENT_PROTOCOL_MAP[clientType];
if (protocol !== 'torrent') {
return;
}
// Get client config and check if postImportCategory is set
const configService = getConfigService();
const manager = getDownloadClientManager(configService);
const clients = await manager.getAllClients();
const clientConfig = clients.find(c => c.enabled && c.type === clientType);
if (!clientConfig?.postImportCategory) {
return;
}
logger.info(`Applying post-import category "${clientConfig.postImportCategory}" to download ${downloadHistory.downloadClientId}`);
const service = await manager.createClientFromConfig(clientConfig);
await service.setCategory(downloadHistory.downloadClientId, clientConfig.postImportCategory);
logger.info(`Post-import category applied successfully`);
} catch (error) {
logger.warn(`Failed to apply post-import category: ${error instanceof Error ? error.message : String(error)}`);
}
}
// =========================================================================
// DOWNLOAD CLEANUP
// =========================================================================