feat(notifications): add unified multi-channel notification system
Introduces a fully featured notification system with three delivery channels (in-app, email, webhook), normalized per-user/per-event/ per-channel preferences, admin-scoped notifications, scheduled trip reminders and version update alerts. - New notificationService.send() as the single orchestration entry point - In-app notifications with simple/boolean/navigate types and WebSocket push - Per-user preference matrix with normalized notification_channel_preferences table - Admin notification preferences stored globally in app_settings - Migration 69 normalizes legacy notification_preferences table - Scheduler hooks for daily trip reminders and version checks - DevNotificationsPanel for testing in dev mode - All new tests passing, covering dispatch, preferences, migration, boolean responses, resilience, and full API integration (NSVC, NPREF, INOTIF, MIGR, VNOTIF, NROUTE series) - Previous tests passing
This commit is contained in:
@@ -480,3 +480,29 @@ export function createInviteToken(
|
||||
).run(token, overrides.max_uses ?? 1, overrides.expires_at ?? null, createdBy);
|
||||
return db.prepare('SELECT * FROM invite_tokens WHERE id = ?').get(result.lastInsertRowid) as TestInviteToken;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Notification helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Upsert a key/value pair into app_settings. */
|
||||
export function setAppSetting(db: Database.Database, key: string, value: string): void {
|
||||
db.prepare('INSERT OR REPLACE INTO app_settings (key, value) VALUES (?, ?)').run(key, value);
|
||||
}
|
||||
|
||||
/** Set the active notification channels (e.g. 'email', 'webhook', 'email,webhook', 'none'). */
|
||||
export function setNotificationChannels(db: Database.Database, channels: string): void {
|
||||
setAppSetting(db, 'notification_channels', channels);
|
||||
}
|
||||
|
||||
/** Explicitly disable a per-user notification preference for a given event+channel combo. */
|
||||
export function disableNotificationPref(
|
||||
db: Database.Database,
|
||||
userId: number,
|
||||
eventType: string,
|
||||
channel: string
|
||||
): void {
|
||||
db.prepare(
|
||||
'INSERT OR REPLACE INTO notification_channel_preferences (user_id, event_type, channel, enabled) VALUES (?, ?, ?, 0)'
|
||||
).run(userId, eventType, channel);
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ const RESET_TABLES = [
|
||||
'vacay_plans',
|
||||
'atlas_visited_countries',
|
||||
'atlas_bucket_list',
|
||||
'notification_channel_preferences',
|
||||
'notifications',
|
||||
'audit_log',
|
||||
'user_settings',
|
||||
|
||||
Reference in New Issue
Block a user