feat: add support for Pushover notification provider

- Updated the list of supported notification provider types to include 'pushover'.
- Enhanced the notifications API tests to validate Pushover integration.
- Modified the notifications form to include fields specific to Pushover, such as API Token and User Key.
- Implemented CRUD operations for Pushover providers in the settings.
- Added end-to-end tests for Pushover provider functionality, including form rendering, payload validation, and security checks.
- Updated translations to include Pushover-specific labels and placeholders.
This commit is contained in:
GitHub Actions
2026-03-16 18:16:14 +00:00
parent 9496001811
commit 65d02e754e
24 changed files with 2464 additions and 275 deletions

View File

@@ -53,7 +53,7 @@ describe('notifications api', () => {
await testProvider({ id: '2', name: 'test', type: 'discord' })
expect(client.post).toHaveBeenCalledWith('/notifications/providers/test', { id: '2', name: 'test', type: 'discord' })
await expect(createProvider({ name: 'x', type: 'pushover' })).rejects.toThrow('Unsupported notification provider type: pushover')
await expect(createProvider({ name: 'x', type: 'pagerduty' })).rejects.toThrow('Unsupported notification provider type: pagerduty')
await expect(updateProvider('2', { name: 'updated', type: 'generic' })).rejects.toThrow('Unsupported notification provider type: generic')
await testProvider({ id: '2', name: 'test', type: 'telegram' })
expect(client.post).toHaveBeenCalledWith('/notifications/providers/test', { id: '2', name: 'test', type: 'telegram' })

View File

@@ -16,6 +16,7 @@ import {
previewExternalTemplate,
getSecurityNotificationSettings,
updateSecurityNotificationSettings,
SUPPORTED_NOTIFICATION_PROVIDER_TYPES,
} from './notifications'
vi.mock('./client', () => ({
@@ -118,7 +119,7 @@ describe('notifications api', () => {
type: 'gotify',
})
await expect(createProvider({ name: 'Bad', type: 'pushover' })).rejects.toThrow('Unsupported notification provider type: pushover')
await expect(createProvider({ name: 'Bad', type: 'sms' })).rejects.toThrow('Unsupported notification provider type: sms')
await expect(updateProvider('bad', { type: 'generic' })).rejects.toThrow('Unsupported notification provider type: generic')
})
@@ -228,4 +229,28 @@ describe('notifications api', () => {
expect(mockedClient.put).toHaveBeenCalledWith('/notifications/settings/security', { enabled: false, min_log_level: 'error' })
expect(updated.enabled).toBe(false)
})
it('pushover is in SUPPORTED_NOTIFICATION_PROVIDER_TYPES', () => {
expect(SUPPORTED_NOTIFICATION_PROVIDER_TYPES).toContain('pushover')
})
it('sanitizeProviderForWriteAction preserves token for pushover type', async () => {
mockedClient.post.mockResolvedValue({ data: { id: 'po1' } })
mockedClient.put.mockResolvedValue({ data: { id: 'po1' } })
await createProvider({ name: 'Pushover', type: 'pushover', gotify_token: 'app-api-token', url: 'uQiRzpo4DXghDmr9QzzfQu27cmVRsG' })
expect(mockedClient.post).toHaveBeenCalledWith('/notifications/providers', {
name: 'Pushover',
type: 'pushover',
token: 'app-api-token',
url: 'uQiRzpo4DXghDmr9QzzfQu27cmVRsG',
})
await updateProvider('po1', { type: 'pushover', url: 'uQiRzpo4DXghDmr9QzzfQu27cmVRsG', gotify_token: 'new-token' })
expect(mockedClient.put).toHaveBeenCalledWith('/notifications/providers/po1', {
type: 'pushover',
url: 'uQiRzpo4DXghDmr9QzzfQu27cmVRsG',
token: 'new-token',
})
})
})

View File

@@ -1,6 +1,6 @@
import client from './client';
export const SUPPORTED_NOTIFICATION_PROVIDER_TYPES = ['discord', 'gotify', 'webhook', 'email', 'telegram', 'slack'] as const;
export const SUPPORTED_NOTIFICATION_PROVIDER_TYPES = ['discord', 'gotify', 'webhook', 'email', 'telegram', 'slack', 'pushover'] as const;
export type SupportedNotificationProviderType = (typeof SUPPORTED_NOTIFICATION_PROVIDER_TYPES)[number];
const DEFAULT_PROVIDER_TYPE: SupportedNotificationProviderType = 'discord';
@@ -59,7 +59,7 @@ const sanitizeProviderForWriteAction = (data: Partial<NotificationProvider>): Pa
delete payload.gotify_token;
if (type !== 'gotify' && type !== 'telegram' && type !== 'slack') {
if (type !== 'gotify' && type !== 'telegram' && type !== 'slack' && type !== 'pushover') {
delete payload.token;
return payload;
}