podverse-notifications 1.0.0 → 5.1.19-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +257 -1
- package/dist/config/index.d.ts +17 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +30 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -1
- package/dist/services/notifications/i18nNotifications.d.ts +4 -0
- package/dist/services/notifications/i18nNotifications.d.ts.map +1 -0
- package/dist/services/notifications/i18nNotifications.js +49 -0
- package/dist/services/notifications/index.d.ts +3 -0
- package/dist/services/notifications/index.d.ts.map +1 -0
- package/dist/services/notifications/index.js +18 -0
- package/dist/services/notifications/notificationOrchestrator.d.ts +33 -0
- package/dist/services/notifications/notificationOrchestrator.d.ts.map +1 -0
- package/dist/services/notifications/notificationOrchestrator.js +97 -0
- package/dist/services/unifiedpush/index.d.ts +4 -0
- package/dist/services/unifiedpush/index.d.ts.map +1 -0
- package/dist/services/unifiedpush/index.js +7 -0
- package/dist/services/unifiedpush/unifiedpushHelpers.d.ts +5 -0
- package/dist/services/unifiedpush/unifiedpushHelpers.d.ts.map +1 -0
- package/dist/services/unifiedpush/unifiedpushHelpers.js +2 -0
- package/dist/services/unifiedpush/unifiedpushNotification.d.ts +25 -0
- package/dist/services/unifiedpush/unifiedpushNotification.d.ts.map +1 -0
- package/dist/services/unifiedpush/unifiedpushNotification.js +90 -0
- package/dist/services/unifiedpush/unifiedpushNotificationOrchestrator.d.ts +15 -0
- package/dist/services/unifiedpush/unifiedpushNotificationOrchestrator.d.ts.map +1 -0
- package/dist/services/unifiedpush/unifiedpushNotificationOrchestrator.js +27 -0
- package/dist/services/webpush/index.d.ts +5 -0
- package/dist/services/webpush/index.d.ts.map +1 -0
- package/dist/services/webpush/index.js +10 -0
- package/dist/services/webpush/webpushAdmin.d.ts +4 -0
- package/dist/services/webpush/webpushAdmin.d.ts.map +1 -0
- package/dist/services/webpush/webpushAdmin.js +28 -0
- package/dist/services/webpush/webpushHelpers.d.ts +8 -0
- package/dist/services/webpush/webpushHelpers.d.ts.map +1 -0
- package/dist/services/webpush/webpushHelpers.js +2 -0
- package/dist/services/webpush/webpushNotification.d.ts +17 -0
- package/dist/services/webpush/webpushNotification.d.ts.map +1 -0
- package/dist/services/webpush/webpushNotification.js +69 -0
- package/dist/services/webpush/webpushNotificationOrchestrator.d.ts +15 -0
- package/dist/services/webpush/webpushNotificationOrchestrator.d.ts.map +1 -0
- package/dist/services/webpush/webpushNotificationOrchestrator.js +27 -0
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -1,2 +1,258 @@
|
|
|
1
1
|
# podverse-notifications
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
Push notification module for Podverse. Handles notification orchestration for multiple services.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Web Push** - Native browser push notifications using the W3C Push API (no third-party dependency)
|
|
8
|
+
- **Firebase FCM** - Firebase Cloud Messaging for mobile apps (via podverse-external-services)
|
|
9
|
+
- **i18n Support** - Localized notification prefixes
|
|
10
|
+
- **Service Orchestration** - Unified API for sending notifications across multiple services
|
|
11
|
+
|
|
12
|
+
## Services
|
|
13
|
+
|
|
14
|
+
### Web Push (Native)
|
|
15
|
+
|
|
16
|
+
Web Push uses the W3C Push API standard. It's built into modern browsers and doesn't require any third-party service. The browser's built-in push service handles message delivery:
|
|
17
|
+
|
|
18
|
+
- **Chrome/Edge**: Google's push service
|
|
19
|
+
- **Firefox**: Mozilla's push service
|
|
20
|
+
- **Safari**: Apple's push service
|
|
21
|
+
|
|
22
|
+
### Firebase FCM (Third-Party)
|
|
23
|
+
|
|
24
|
+
Firebase Cloud Messaging is used for mobile app notifications (iOS/Android). This is handled by `podverse-external-services` since it's a third-party dependency.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install podverse-notifications
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Configuration
|
|
33
|
+
|
|
34
|
+
### Environment Variables
|
|
35
|
+
|
|
36
|
+
```env
|
|
37
|
+
# Web Push Configuration
|
|
38
|
+
WEBPUSH_ENABLED=true
|
|
39
|
+
WEBPUSH_VAPID_PUBLIC_KEY=your-vapid-public-key
|
|
40
|
+
WEBPUSH_VAPID_PRIVATE_KEY=your-vapid-private-key
|
|
41
|
+
WEBPUSH_VAPID_SUBJECT=mailto:contact@podverse.fm
|
|
42
|
+
|
|
43
|
+
# Web URL Configuration (for notification links)
|
|
44
|
+
WEB_PROTOCOL=https
|
|
45
|
+
WEB_DOMAIN=podverse.fm
|
|
46
|
+
WEB_ICON_IMAGE_PATH=https://podverse.fm/favicon/web-app-manifest-192x192.png
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Environment-Specific Configuration
|
|
50
|
+
|
|
51
|
+
#### Local Development
|
|
52
|
+
|
|
53
|
+
For local development, set environment variables in your `.env` file or shell:
|
|
54
|
+
|
|
55
|
+
```env
|
|
56
|
+
WEBPUSH_ENABLED=true
|
|
57
|
+
WEBPUSH_VAPID_PUBLIC_KEY=your-dev-vapid-public-key
|
|
58
|
+
WEBPUSH_VAPID_PRIVATE_KEY=your-dev-vapid-private-key
|
|
59
|
+
WEBPUSH_VAPID_SUBJECT=mailto:dev@example.com
|
|
60
|
+
|
|
61
|
+
WEB_PROTOCOL=http
|
|
62
|
+
WEB_DOMAIN=localhost:3000
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### Alpha/Staging
|
|
66
|
+
|
|
67
|
+
For alpha/staging environments, configure via Docker environment or secrets:
|
|
68
|
+
|
|
69
|
+
```env
|
|
70
|
+
WEBPUSH_ENABLED=true
|
|
71
|
+
WEBPUSH_VAPID_PUBLIC_KEY=your-alpha-vapid-public-key
|
|
72
|
+
WEBPUSH_VAPID_PRIVATE_KEY=your-alpha-vapid-private-key
|
|
73
|
+
WEBPUSH_VAPID_SUBJECT=mailto:contact@podverse.fm
|
|
74
|
+
|
|
75
|
+
WEB_PROTOCOL=https
|
|
76
|
+
WEB_DOMAIN=alpha.podverse.fm
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
#### Production
|
|
80
|
+
|
|
81
|
+
For production, use secrets management (Kubernetes secrets, Docker secrets, etc.):
|
|
82
|
+
|
|
83
|
+
```env
|
|
84
|
+
WEBPUSH_ENABLED=true
|
|
85
|
+
WEBPUSH_VAPID_PUBLIC_KEY=your-production-vapid-public-key
|
|
86
|
+
WEBPUSH_VAPID_PRIVATE_KEY=your-production-vapid-private-key # Keep secret!
|
|
87
|
+
WEBPUSH_VAPID_SUBJECT=mailto:contact@podverse.fm
|
|
88
|
+
|
|
89
|
+
WEB_PROTOCOL=https
|
|
90
|
+
WEB_DOMAIN=podverse.fm
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Key Matching Requirement
|
|
94
|
+
|
|
95
|
+
**Important:** The VAPID public key in `podverse-notifications` (backend) must match the key in `podverse-web` (frontend):
|
|
96
|
+
|
|
97
|
+
| Service | Environment Variable | Key Type |
|
|
98
|
+
|---------|---------------------|----------|
|
|
99
|
+
| podverse-web | `NEXT_PUBLIC_WEBPUSH_VAPID_PUBLIC_KEY` | Public only |
|
|
100
|
+
| podverse-notifications | `WEBPUSH_VAPID_PUBLIC_KEY` | Public |
|
|
101
|
+
| podverse-notifications | `WEBPUSH_VAPID_PRIVATE_KEY` | Private (secret) |
|
|
102
|
+
|
|
103
|
+
If keys don't match, push notifications will fail with authentication errors.
|
|
104
|
+
|
|
105
|
+
## Generating VAPID Keys
|
|
106
|
+
|
|
107
|
+
VAPID (Voluntary Application Server Identification) keys are required for Web Push notifications.
|
|
108
|
+
|
|
109
|
+
### Method 1: Using web-push CLI (Recommended)
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# Install web-push globally
|
|
113
|
+
npm install -g web-push
|
|
114
|
+
|
|
115
|
+
# Generate VAPID keys
|
|
116
|
+
web-push generate-vapid-keys
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Output:
|
|
120
|
+
```
|
|
121
|
+
=======================================
|
|
122
|
+
|
|
123
|
+
Public Key:
|
|
124
|
+
BNxIq7...your-public-key...
|
|
125
|
+
|
|
126
|
+
Private Key:
|
|
127
|
+
AkT3Xy...your-private-key...
|
|
128
|
+
|
|
129
|
+
=======================================
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Method 2: Using npx (No Install)
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npx web-push generate-vapid-keys
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Method 3: Using Node.js Script
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
const webpush = require('web-push');
|
|
142
|
+
const keys = webpush.generateVAPIDKeys();
|
|
143
|
+
console.log('Public Key:', keys.publicKey);
|
|
144
|
+
console.log('Private Key:', keys.privateKey);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Important Notes
|
|
148
|
+
|
|
149
|
+
- **Keep the private key secret** - Only the backend needs it
|
|
150
|
+
- **Use the same key pair everywhere** - Frontend (public only) and backend (public + private) must use the same key pair
|
|
151
|
+
- **Don't regenerate unnecessarily** - Changing keys invalidates all existing push subscriptions
|
|
152
|
+
- **VAPID subject** - Should be a `mailto:` or `https://` URL identifying your service
|
|
153
|
+
|
|
154
|
+
## Usage
|
|
155
|
+
|
|
156
|
+
### Notification Orchestrator
|
|
157
|
+
|
|
158
|
+
The main entry point for sending notifications:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import { notificationOrchestrator } from 'podverse-notifications';
|
|
162
|
+
|
|
163
|
+
// Send Web Push notifications (native, no third-party)
|
|
164
|
+
// The endpoint URL is provided by the browser when the user subscribes
|
|
165
|
+
await notificationOrchestrator({
|
|
166
|
+
service: 'webpush',
|
|
167
|
+
subscriptions: [
|
|
168
|
+
{
|
|
169
|
+
endpoint: 'https://push.example.com/...', // Browser-provided endpoint
|
|
170
|
+
keys: {
|
|
171
|
+
p256dh: 'public-key-from-subscription',
|
|
172
|
+
auth: 'auth-secret-from-subscription'
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
],
|
|
176
|
+
messageText: 'New Episode: My Podcast',
|
|
177
|
+
messageType: 'new-episode',
|
|
178
|
+
locale: 'en',
|
|
179
|
+
linkIdText: 'episode-id-text'
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Send Firebase FCM notifications (for mobile apps)
|
|
183
|
+
await notificationOrchestrator({
|
|
184
|
+
service: 'firebase',
|
|
185
|
+
tokens: ['fcm-token-1', 'fcm-token-2'],
|
|
186
|
+
platform: 'android', // or 'ios'
|
|
187
|
+
messageText: 'New Episode: My Podcast',
|
|
188
|
+
messageType: 'new-episode',
|
|
189
|
+
locale: 'en',
|
|
190
|
+
linkIdText: 'episode-id-text'
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Message Types
|
|
195
|
+
|
|
196
|
+
Supported notification message types:
|
|
197
|
+
|
|
198
|
+
| Type | Description |
|
|
199
|
+
|------|-------------|
|
|
200
|
+
| `new` | Generic new content |
|
|
201
|
+
| `new-episode` | New podcast episode |
|
|
202
|
+
| `new-podcast` | New podcast added |
|
|
203
|
+
| `new-video` | New video |
|
|
204
|
+
| `new-video-channel` | New video channel |
|
|
205
|
+
| `new-track` | New music track |
|
|
206
|
+
| `new-album` | New music album |
|
|
207
|
+
| `livestream-started` | Livestream is now live |
|
|
208
|
+
| `livestream-scheduled` | Livestream scheduled |
|
|
209
|
+
|
|
210
|
+
### Direct Web Push API
|
|
211
|
+
|
|
212
|
+
For lower-level access:
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
import {
|
|
216
|
+
sendWebPushNotificationBatch,
|
|
217
|
+
WebPushSubscription
|
|
218
|
+
} from 'podverse-notifications';
|
|
219
|
+
|
|
220
|
+
const subscriptions: WebPushSubscription[] = [
|
|
221
|
+
{
|
|
222
|
+
endpoint: 'https://...',
|
|
223
|
+
keys: { p256dh: '...', auth: '...' }
|
|
224
|
+
}
|
|
225
|
+
];
|
|
226
|
+
|
|
227
|
+
const results = await sendWebPushNotificationBatch(subscriptions, {
|
|
228
|
+
title: 'Notification Title',
|
|
229
|
+
body: 'Notification body text',
|
|
230
|
+
icon: 'https://example.com/icon.png',
|
|
231
|
+
link: '/episode/abc123'
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Architecture
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
podverse-notifications/
|
|
239
|
+
├── src/
|
|
240
|
+
│ ├── config/
|
|
241
|
+
│ │ └── index.ts # Configuration and URL helpers
|
|
242
|
+
│ ├── services/
|
|
243
|
+
│ │ ├── notifications/
|
|
244
|
+
│ │ │ ├── i18nNotifications.ts # Localized message prefixes
|
|
245
|
+
│ │ │ ├── notificationOrchestrator.ts # Main orchestrator
|
|
246
|
+
│ │ │ └── index.ts
|
|
247
|
+
│ │ └── webpush/
|
|
248
|
+
│ │ ├── webpushAdmin.ts # web-push initialization
|
|
249
|
+
│ │ ├── webpushHelpers.ts # Types and utilities
|
|
250
|
+
│ │ ├── webpushNotification.ts # Send notifications
|
|
251
|
+
│ │ ├── webpushNotificationOrchestrator.ts
|
|
252
|
+
│ │ └── index.ts
|
|
253
|
+
│ └── index.ts
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## License
|
|
257
|
+
|
|
258
|
+
AGPLv3
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const config: {
|
|
2
|
+
web: {
|
|
3
|
+
protocol: string;
|
|
4
|
+
host: string;
|
|
5
|
+
icon_image_path: string;
|
|
6
|
+
};
|
|
7
|
+
webpush: {
|
|
8
|
+
enabled: boolean;
|
|
9
|
+
vapid_public_key: string;
|
|
10
|
+
vapid_private_key: string;
|
|
11
|
+
vapid_subject: string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
export declare function getWebBaseUrl(): string;
|
|
15
|
+
export declare function getWebBaseUrlWithPath(path: string): string;
|
|
16
|
+
export declare function getWebIconImageUrl(): string;
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,MAAM;;;;;;;;;;;;CAYlB,CAAA;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI1D;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.config = void 0;
|
|
4
|
+
exports.getWebBaseUrl = getWebBaseUrl;
|
|
5
|
+
exports.getWebBaseUrlWithPath = getWebBaseUrlWithPath;
|
|
6
|
+
exports.getWebIconImageUrl = getWebIconImageUrl;
|
|
7
|
+
exports.config = {
|
|
8
|
+
web: {
|
|
9
|
+
protocol: process.env.WEB_PROTOCOL || "http",
|
|
10
|
+
host: process.env.WEB_DOMAIN || "localhost",
|
|
11
|
+
icon_image_path: process.env.WEB_ICON_IMAGE_PATH || ""
|
|
12
|
+
},
|
|
13
|
+
webpush: {
|
|
14
|
+
enabled: process.env.WEBPUSH_ENABLED === "true",
|
|
15
|
+
vapid_public_key: process.env.WEBPUSH_VAPID_PUBLIC_KEY || "",
|
|
16
|
+
vapid_private_key: process.env.WEBPUSH_VAPID_PRIVATE_KEY || "",
|
|
17
|
+
vapid_subject: process.env.WEBPUSH_VAPID_SUBJECT || "mailto:contact@podverse.fm"
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
function getWebBaseUrl() {
|
|
21
|
+
return `${exports.config.web.protocol}://${exports.config.web.host}`;
|
|
22
|
+
}
|
|
23
|
+
function getWebBaseUrlWithPath(path) {
|
|
24
|
+
const base = getWebBaseUrl();
|
|
25
|
+
const cleanPath = path.startsWith('/') ? path : `/${path}`;
|
|
26
|
+
return `${base}${cleanPath}`;
|
|
27
|
+
}
|
|
28
|
+
function getWebIconImageUrl() {
|
|
29
|
+
return `${getWebBaseUrl()}${exports.config.web.icon_image_path}`;
|
|
30
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./config"), exports);
|
|
18
|
+
__exportStar(require("./services/notifications"), exports);
|
|
19
|
+
__exportStar(require("./services/webpush"), exports);
|
|
20
|
+
__exportStar(require("./services/unifiedpush"), exports);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type NotificationMessageType = 'new' | 'new-episode' | 'new-podcast' | 'new-video' | 'new-video-channel' | 'new-track' | 'new-album' | 'livestream-started' | 'livestream-scheduled';
|
|
2
|
+
export type NotificationLocaleMap = Record<NotificationMessageType, string>;
|
|
3
|
+
export declare const i18nNotifications: Record<string, NotificationLocaleMap>;
|
|
4
|
+
//# sourceMappingURL=i18nNotifications.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18nNotifications.d.ts","sourceRoot":"","sources":["../../../src/services/notifications/i18nNotifications.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,uBAAuB,GAAG,KAAK,GACvC,aAAa,GAAG,aAAa,GAC7B,WAAW,GAAG,mBAAmB,GACjC,WAAW,GAAG,WAAW,GACzB,oBAAoB,GAAG,sBAAsB,CAAC;AAElD,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC;AAE5E,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CA6CnE,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.i18nNotifications = void 0;
|
|
4
|
+
exports.i18nNotifications = {
|
|
5
|
+
"en": {
|
|
6
|
+
"new": "",
|
|
7
|
+
"new-episode": "",
|
|
8
|
+
"new-podcast": "",
|
|
9
|
+
"new-video": "",
|
|
10
|
+
"new-video-channel": "",
|
|
11
|
+
"new-track": "",
|
|
12
|
+
"new-album": "",
|
|
13
|
+
"livestream-started": "Live: ",
|
|
14
|
+
"livestream-scheduled": "Live Scheduled: "
|
|
15
|
+
},
|
|
16
|
+
"es": {
|
|
17
|
+
"new": "",
|
|
18
|
+
"new-episode": "",
|
|
19
|
+
"new-podcast": "",
|
|
20
|
+
"new-video": "",
|
|
21
|
+
"new-video-channel": "",
|
|
22
|
+
"new-track": "",
|
|
23
|
+
"new-album": "",
|
|
24
|
+
"livestream-started": "En vivo: ",
|
|
25
|
+
"livestream-scheduled": "En vivo programado: "
|
|
26
|
+
},
|
|
27
|
+
"fr": {
|
|
28
|
+
"new": "",
|
|
29
|
+
"new-episode": "",
|
|
30
|
+
"new-podcast": "",
|
|
31
|
+
"new-video": "",
|
|
32
|
+
"new-video-channel": "",
|
|
33
|
+
"new-track": "",
|
|
34
|
+
"new-album": "",
|
|
35
|
+
"livestream-started": "En direct: ",
|
|
36
|
+
"livestream-scheduled": "En direct programmé: "
|
|
37
|
+
},
|
|
38
|
+
"el-GR": {
|
|
39
|
+
"new": "",
|
|
40
|
+
"new-episode": "",
|
|
41
|
+
"new-podcast": "",
|
|
42
|
+
"new-video": "",
|
|
43
|
+
"new-video-channel": "",
|
|
44
|
+
"new-track": "",
|
|
45
|
+
"new-album": "",
|
|
46
|
+
"livestream-started": "Ζωντανά: ",
|
|
47
|
+
"livestream-scheduled": "Προγραμματισμένα ζωντανά: "
|
|
48
|
+
}
|
|
49
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/notifications/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./i18nNotifications"), exports);
|
|
18
|
+
__exportStar(require("./notificationOrchestrator"), exports);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { WebPushSubscription } from '../webpush';
|
|
2
|
+
import { UPSubscription } from '../unifiedpush';
|
|
3
|
+
import { NotificationMessageType } from './i18nNotifications';
|
|
4
|
+
export type NotificationPlatform = 'web' | 'android' | 'ios';
|
|
5
|
+
export type NotificationService = 'firebase' | 'webpush' | 'unifiedpush';
|
|
6
|
+
type BaseNotificationOrchestratorParams = {
|
|
7
|
+
messageText: string;
|
|
8
|
+
messageType: NotificationMessageType;
|
|
9
|
+
locale: string;
|
|
10
|
+
icon?: string;
|
|
11
|
+
linkIdText?: string;
|
|
12
|
+
data?: Record<string, unknown>;
|
|
13
|
+
};
|
|
14
|
+
type FirebaseNotificationOrchestratorParams = BaseNotificationOrchestratorParams & {
|
|
15
|
+
service: 'firebase';
|
|
16
|
+
tokens: string[];
|
|
17
|
+
platform: NotificationPlatform;
|
|
18
|
+
channelId?: string;
|
|
19
|
+
badge?: number;
|
|
20
|
+
sound?: string;
|
|
21
|
+
};
|
|
22
|
+
type WebPushNotificationOrchestratorParams = BaseNotificationOrchestratorParams & {
|
|
23
|
+
service: 'webpush';
|
|
24
|
+
subscriptions: WebPushSubscription[];
|
|
25
|
+
};
|
|
26
|
+
type UnifiedPushNotificationOrchestratorParams = BaseNotificationOrchestratorParams & {
|
|
27
|
+
service: 'unifiedpush';
|
|
28
|
+
subscriptions: UPSubscription[];
|
|
29
|
+
};
|
|
30
|
+
export type NotificationOrchestratorParams = FirebaseNotificationOrchestratorParams | WebPushNotificationOrchestratorParams | UnifiedPushNotificationOrchestratorParams;
|
|
31
|
+
export declare function notificationOrchestrator(params: NotificationOrchestratorParams): Promise<any[]>;
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=notificationOrchestrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notificationOrchestrator.d.ts","sourceRoot":"","sources":["../../../src/services/notifications/notificationOrchestrator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEjD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAqB,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAEjF,MAAM,MAAM,oBAAoB,GAAG,KAAK,GAAG,SAAS,GAAG,KAAK,CAAC;AAC7D,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,SAAS,GAAG,aAAa,CAAC;AA6BzE,KAAK,kCAAkC,GAAG;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,uBAAuB,CAAC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AAGF,KAAK,sCAAsC,GAAG,kCAAkC,GAAG;IACjF,OAAO,EAAE,UAAU,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAGF,KAAK,qCAAqC,GAAG,kCAAkC,GAAG;IAChF,OAAO,EAAE,SAAS,CAAC;IACnB,aAAa,EAAE,mBAAmB,EAAE,CAAC;CACtC,CAAC;AAGF,KAAK,yCAAyC,GAAG,kCAAkC,GAAG;IACpF,OAAO,EAAE,aAAa,CAAC;IACvB,aAAa,EAAE,cAAc,EAAE,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,8BAA8B,GACtC,sCAAsC,GACtC,qCAAqC,GACrC,yCAAyC,CAAC;AAS9C,wBAAsB,wBAAwB,CAAC,MAAM,EAAE,8BAA8B,kBAoDpF"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.notificationOrchestrator = notificationOrchestrator;
|
|
13
|
+
const podverse_external_services_1 = require("podverse-external-services");
|
|
14
|
+
const webpush_1 = require("../webpush");
|
|
15
|
+
const unifiedpush_1 = require("../unifiedpush");
|
|
16
|
+
const i18nNotifications_1 = require("./i18nNotifications");
|
|
17
|
+
/**
|
|
18
|
+
* Gets the URL path prefix for a given notification message type
|
|
19
|
+
*/
|
|
20
|
+
function getLinkPathFromMessageType(messageType) {
|
|
21
|
+
switch (messageType) {
|
|
22
|
+
case 'new-episode':
|
|
23
|
+
return '/episode';
|
|
24
|
+
case 'new-podcast':
|
|
25
|
+
return '/podcast';
|
|
26
|
+
case 'new-video':
|
|
27
|
+
return '/video';
|
|
28
|
+
case 'new-video-channel':
|
|
29
|
+
return '/channel';
|
|
30
|
+
case 'new-track':
|
|
31
|
+
return '/track';
|
|
32
|
+
case 'new-album':
|
|
33
|
+
return '/album';
|
|
34
|
+
case 'livestream-started':
|
|
35
|
+
case 'livestream-scheduled':
|
|
36
|
+
return '/livestream';
|
|
37
|
+
case 'new':
|
|
38
|
+
default:
|
|
39
|
+
return '';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function getFinalText(messageText, messageType, locale) {
|
|
43
|
+
const baseLocale = locale.includes('-') ? locale.split('-')[0] : locale;
|
|
44
|
+
const localeMap = i18nNotifications_1.i18nNotifications[locale] || i18nNotifications_1.i18nNotifications[baseLocale] || i18nNotifications_1.i18nNotifications.en;
|
|
45
|
+
const prefix = localeMap[messageType] || i18nNotifications_1.i18nNotifications.en[messageType];
|
|
46
|
+
return `${prefix}${messageText}`;
|
|
47
|
+
}
|
|
48
|
+
function notificationOrchestrator(params) {
|
|
49
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
50
|
+
const { service, messageText, messageType, locale, linkIdText, icon, data } = params;
|
|
51
|
+
const finalText = getFinalText(messageText, messageType, locale);
|
|
52
|
+
// Construct the link from messageType and linkIdText
|
|
53
|
+
let link;
|
|
54
|
+
if (linkIdText) {
|
|
55
|
+
const pathPrefix = getLinkPathFromMessageType(messageType);
|
|
56
|
+
link = pathPrefix ? `${pathPrefix}/${linkIdText}` : undefined;
|
|
57
|
+
}
|
|
58
|
+
switch (service) {
|
|
59
|
+
case 'firebase': {
|
|
60
|
+
const firebaseParams = params;
|
|
61
|
+
return yield (0, podverse_external_services_1.firebaseNotificationBatchOrchestrator)({
|
|
62
|
+
tokens: firebaseParams.tokens,
|
|
63
|
+
platform: firebaseParams.platform,
|
|
64
|
+
finalText,
|
|
65
|
+
link,
|
|
66
|
+
icon,
|
|
67
|
+
channelId: firebaseParams.channelId,
|
|
68
|
+
badge: firebaseParams.badge,
|
|
69
|
+
sound: firebaseParams.sound,
|
|
70
|
+
data,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
case 'webpush': {
|
|
74
|
+
const webpushParams = params;
|
|
75
|
+
return yield (0, webpush_1.webpushNotificationBatchOrchestrator)({
|
|
76
|
+
subscriptions: webpushParams.subscriptions,
|
|
77
|
+
finalText,
|
|
78
|
+
link,
|
|
79
|
+
icon,
|
|
80
|
+
data,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
case 'unifiedpush': {
|
|
84
|
+
const upParams = params;
|
|
85
|
+
return yield (0, unifiedpush_1.unifiedpushNotificationBatchOrchestrator)({
|
|
86
|
+
subscriptions: upParams.subscriptions,
|
|
87
|
+
finalText,
|
|
88
|
+
link,
|
|
89
|
+
icon,
|
|
90
|
+
data,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
default:
|
|
94
|
+
throw new Error(`Unsupported notification service: ${service}`);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/unifiedpush/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,wCAAwC,EAAE,MAAM,uCAAuC,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.unifiedpushNotificationBatchOrchestrator = exports.sendUPNotificationBatch = void 0;
|
|
4
|
+
var unifiedpushNotification_1 = require("./unifiedpushNotification");
|
|
5
|
+
Object.defineProperty(exports, "sendUPNotificationBatch", { enumerable: true, get: function () { return unifiedpushNotification_1.sendUPNotificationBatch; } });
|
|
6
|
+
var unifiedpushNotificationOrchestrator_1 = require("./unifiedpushNotificationOrchestrator");
|
|
7
|
+
Object.defineProperty(exports, "unifiedpushNotificationBatchOrchestrator", { enumerable: true, get: function () { return unifiedpushNotificationOrchestrator_1.unifiedpushNotificationBatchOrchestrator; } });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unifiedpushHelpers.d.ts","sourceRoot":"","sources":["../../../src/services/unifiedpush/unifiedpushHelpers.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { UPSubscription } from './unifiedpushHelpers';
|
|
2
|
+
type UPPayload = {
|
|
3
|
+
title: string;
|
|
4
|
+
body?: string;
|
|
5
|
+
icon?: string;
|
|
6
|
+
image?: string;
|
|
7
|
+
link?: string;
|
|
8
|
+
data?: Record<string, unknown>;
|
|
9
|
+
};
|
|
10
|
+
type UPResult = {
|
|
11
|
+
success: boolean;
|
|
12
|
+
endpoint: string;
|
|
13
|
+
error?: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Sends a notification to a Unified Push endpoint.
|
|
17
|
+
*
|
|
18
|
+
* Unified Push is a simple protocol - just POST the notification data to the endpoint.
|
|
19
|
+
* The endpoint is typically a service like ntfy.sh that the user has configured.
|
|
20
|
+
*
|
|
21
|
+
* @see https://unifiedpush.org/spec/android/
|
|
22
|
+
*/
|
|
23
|
+
export declare function sendUPNotificationBatch(subscriptions: UPSubscription[], payload: UPPayload): Promise<UPResult[]>;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=unifiedpushNotification.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unifiedpushNotification.d.ts","sourceRoot":"","sources":["../../../src/services/unifiedpush/unifiedpushNotification.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,KAAK,SAAS,GAAG;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AAEF,KAAK,QAAQ,GAAG;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAsB,uBAAuB,CAC3C,aAAa,EAAE,cAAc,EAAE,EAC/B,OAAO,EAAE,SAAS,GACjB,OAAO,CAAC,QAAQ,EAAE,CAAC,CAyErB"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.sendUPNotificationBatch = sendUPNotificationBatch;
|
|
13
|
+
const podverse_helpers_1 = require("podverse-helpers");
|
|
14
|
+
const config_1 = require("../../config");
|
|
15
|
+
/**
|
|
16
|
+
* Sends a notification to a Unified Push endpoint.
|
|
17
|
+
*
|
|
18
|
+
* Unified Push is a simple protocol - just POST the notification data to the endpoint.
|
|
19
|
+
* The endpoint is typically a service like ntfy.sh that the user has configured.
|
|
20
|
+
*
|
|
21
|
+
* @see https://unifiedpush.org/spec/android/
|
|
22
|
+
*/
|
|
23
|
+
function sendUPNotificationBatch(subscriptions, payload) {
|
|
24
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
25
|
+
var _a;
|
|
26
|
+
const chunks = (0, podverse_helpers_1.chunkArray)(subscriptions, 100);
|
|
27
|
+
const allResults = [];
|
|
28
|
+
for (const chunk of chunks) {
|
|
29
|
+
const chunkResults = yield Promise.allSettled(chunk.map((subscription) => __awaiter(this, void 0, void 0, function* () {
|
|
30
|
+
try {
|
|
31
|
+
// Use ntfy header-based format for proper notification display
|
|
32
|
+
// @see https://docs.ntfy.sh/publish/#publish-as-json
|
|
33
|
+
const headers = {
|
|
34
|
+
'Content-Type': 'text/plain',
|
|
35
|
+
'X-Title': payload.title,
|
|
36
|
+
'X-Click': payload.link ? (0, config_1.getWebBaseUrlWithPath)(payload.link) : (0, config_1.getWebBaseUrl)(),
|
|
37
|
+
'X-Icon': payload.icon || (0, config_1.getWebIconImageUrl)(),
|
|
38
|
+
};
|
|
39
|
+
// Add image as attachment for preview
|
|
40
|
+
if (payload.image) {
|
|
41
|
+
headers['X-Attach'] = payload.image;
|
|
42
|
+
}
|
|
43
|
+
// Add authorization if auth key is provided
|
|
44
|
+
if (subscription.up_auth_key) {
|
|
45
|
+
headers['Authorization'] = `Bearer ${subscription.up_auth_key}`;
|
|
46
|
+
}
|
|
47
|
+
// Message body is sent as plain text
|
|
48
|
+
const messageBody = payload.body || '';
|
|
49
|
+
const response = yield fetch(subscription.up_endpoint, {
|
|
50
|
+
method: 'POST',
|
|
51
|
+
headers,
|
|
52
|
+
body: messageBody,
|
|
53
|
+
});
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
const errorText = yield response.text().catch(() => 'Unknown error');
|
|
56
|
+
console.error(`UP send failed for ${subscription.up_endpoint}: ${response.status} - ${errorText}`);
|
|
57
|
+
return {
|
|
58
|
+
success: false,
|
|
59
|
+
endpoint: subscription.up_endpoint,
|
|
60
|
+
error: `HTTP ${response.status}: ${errorText}`
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return { success: true, endpoint: subscription.up_endpoint };
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
67
|
+
console.error(`UP send failed for ${subscription.up_endpoint}:`, errorMessage);
|
|
68
|
+
return {
|
|
69
|
+
success: false,
|
|
70
|
+
endpoint: subscription.up_endpoint,
|
|
71
|
+
error: errorMessage
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
})));
|
|
75
|
+
for (const result of chunkResults) {
|
|
76
|
+
if (result.status === 'fulfilled') {
|
|
77
|
+
allResults.push(result.value);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
allResults.push({
|
|
81
|
+
success: false,
|
|
82
|
+
endpoint: 'unknown',
|
|
83
|
+
error: ((_a = result.reason) === null || _a === void 0 ? void 0 : _a.message) || 'Promise rejected'
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return allResults;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { UPSubscription } from './unifiedpushHelpers';
|
|
2
|
+
type UPOrchestratorParams = {
|
|
3
|
+
subscriptions: UPSubscription[];
|
|
4
|
+
finalText: string;
|
|
5
|
+
icon?: string;
|
|
6
|
+
link?: string;
|
|
7
|
+
data?: Record<string, unknown>;
|
|
8
|
+
};
|
|
9
|
+
export declare function unifiedpushNotificationBatchOrchestrator(params: UPOrchestratorParams): Promise<{
|
|
10
|
+
success: boolean;
|
|
11
|
+
endpoint: string;
|
|
12
|
+
error?: string;
|
|
13
|
+
}[]>;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=unifiedpushNotificationOrchestrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unifiedpushNotificationOrchestrator.d.ts","sourceRoot":"","sources":["../../../src/services/unifiedpush/unifiedpushNotificationOrchestrator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,KAAK,oBAAoB,GAAG;IAC1B,aAAa,EAAE,cAAc,EAAE,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AAEF,wBAAsB,wCAAwC,CAAC,MAAM,EAAE,oBAAoB;;;;KAc1F"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.unifiedpushNotificationBatchOrchestrator = unifiedpushNotificationBatchOrchestrator;
|
|
13
|
+
const unifiedpushNotification_1 = require("./unifiedpushNotification");
|
|
14
|
+
function unifiedpushNotificationBatchOrchestrator(params) {
|
|
15
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
16
|
+
const { subscriptions, finalText, icon, link, data } = params;
|
|
17
|
+
const payload = {
|
|
18
|
+
title: finalText,
|
|
19
|
+
body: '',
|
|
20
|
+
icon,
|
|
21
|
+
link,
|
|
22
|
+
data,
|
|
23
|
+
};
|
|
24
|
+
console.log(`[unifiedpushNotificationBatchOrchestrator] Sending to ${subscriptions.length} subscriptions`);
|
|
25
|
+
return yield (0, unifiedpushNotification_1.sendUPNotificationBatch)(subscriptions, payload);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { webpushAdmin, isWebPushEnabled } from './webpushAdmin';
|
|
2
|
+
export { WebPushSubscription } from './webpushHelpers';
|
|
3
|
+
export { sendWebPushNotificationBatch } from './webpushNotification';
|
|
4
|
+
export { webpushNotificationBatchOrchestrator } from './webpushNotificationOrchestrator';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/webpush/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,oCAAoC,EAAE,MAAM,mCAAmC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.webpushNotificationBatchOrchestrator = exports.sendWebPushNotificationBatch = exports.isWebPushEnabled = exports.webpushAdmin = void 0;
|
|
4
|
+
var webpushAdmin_1 = require("./webpushAdmin");
|
|
5
|
+
Object.defineProperty(exports, "webpushAdmin", { enumerable: true, get: function () { return webpushAdmin_1.webpushAdmin; } });
|
|
6
|
+
Object.defineProperty(exports, "isWebPushEnabled", { enumerable: true, get: function () { return webpushAdmin_1.isWebPushEnabled; } });
|
|
7
|
+
var webpushNotification_1 = require("./webpushNotification");
|
|
8
|
+
Object.defineProperty(exports, "sendWebPushNotificationBatch", { enumerable: true, get: function () { return webpushNotification_1.sendWebPushNotificationBatch; } });
|
|
9
|
+
var webpushNotificationOrchestrator_1 = require("./webpushNotificationOrchestrator");
|
|
10
|
+
Object.defineProperty(exports, "webpushNotificationBatchOrchestrator", { enumerable: true, get: function () { return webpushNotificationOrchestrator_1.webpushNotificationBatchOrchestrator; } });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webpushAdmin.d.ts","sourceRoot":"","sources":["../../../src/services/webpush/webpushAdmin.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,UAAU,CAAC;AAyB/B,eAAO,MAAM,YAAY,uBAAsC,CAAC;AAChE,eAAO,MAAM,gBAAgB,SAAqB,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isWebPushEnabled = exports.webpushAdmin = void 0;
|
|
7
|
+
const web_push_1 = __importDefault(require("web-push"));
|
|
8
|
+
const config_1 = require("../../config");
|
|
9
|
+
let webpushInitialized = false;
|
|
10
|
+
if (!config_1.config.webpush.enabled) {
|
|
11
|
+
console.warn("Web Push notifications are disabled in the configuration.");
|
|
12
|
+
}
|
|
13
|
+
else if (!config_1.config.webpush.vapid_public_key || !config_1.config.webpush.vapid_private_key) {
|
|
14
|
+
console.warn("Web Push VAPID keys are not configured.");
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
console.log("Web Push notifications are enabled in the configuration.");
|
|
18
|
+
try {
|
|
19
|
+
web_push_1.default.setVapidDetails(config_1.config.webpush.vapid_subject, config_1.config.webpush.vapid_public_key, config_1.config.webpush.vapid_private_key);
|
|
20
|
+
webpushInitialized = true;
|
|
21
|
+
console.log("Web Push Admin Initialized Successfully");
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
console.error("Web Push Admin Initialization Failed:", error);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.webpushAdmin = webpushInitialized ? web_push_1.default : null;
|
|
28
|
+
exports.isWebPushEnabled = webpushInitialized;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webpushHelpers.d.ts","sourceRoot":"","sources":["../../../src/services/webpush/webpushHelpers.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE;QACJ,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { WebPushSubscription } from './webpushHelpers';
|
|
2
|
+
type WebPushPayload = {
|
|
3
|
+
title: string;
|
|
4
|
+
body?: string;
|
|
5
|
+
icon?: string;
|
|
6
|
+
image?: string;
|
|
7
|
+
link?: string;
|
|
8
|
+
data?: Record<string, unknown>;
|
|
9
|
+
};
|
|
10
|
+
type WebPushResult = {
|
|
11
|
+
success: boolean;
|
|
12
|
+
endpoint: string;
|
|
13
|
+
error?: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function sendWebPushNotificationBatch(subscriptions: WebPushSubscription[], payload: WebPushPayload): Promise<WebPushResult[]>;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=webpushNotification.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webpushNotification.d.ts","sourceRoot":"","sources":["../../../src/services/webpush/webpushNotification.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAGvD,KAAK,cAAc,GAAG;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AAEF,KAAK,aAAa,GAAG;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAsB,4BAA4B,CAChD,aAAa,EAAE,mBAAmB,EAAE,EACpC,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,EAAE,CAAC,CA2D1B"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.sendWebPushNotificationBatch = sendWebPushNotificationBatch;
|
|
13
|
+
const podverse_helpers_1 = require("podverse-helpers");
|
|
14
|
+
const webpushAdmin_1 = require("./webpushAdmin");
|
|
15
|
+
const config_1 = require("../../config");
|
|
16
|
+
function sendWebPushNotificationBatch(subscriptions, payload) {
|
|
17
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18
|
+
var _a;
|
|
19
|
+
if (!webpushAdmin_1.webpushAdmin) {
|
|
20
|
+
throw new Error("Web Push Admin is not initialized");
|
|
21
|
+
}
|
|
22
|
+
const webpush = webpushAdmin_1.webpushAdmin;
|
|
23
|
+
const chunks = (0, podverse_helpers_1.chunkArray)(subscriptions, 100);
|
|
24
|
+
const allResults = [];
|
|
25
|
+
for (const chunk of chunks) {
|
|
26
|
+
const notificationPayload = JSON.stringify({
|
|
27
|
+
title: payload.title,
|
|
28
|
+
body: payload.body || "",
|
|
29
|
+
icon: payload.icon || (0, config_1.getWebIconImageUrl)(),
|
|
30
|
+
image: payload.image,
|
|
31
|
+
link: payload.link ? (0, config_1.getWebBaseUrlWithPath)(payload.link) : (0, config_1.getWebBaseUrl)(),
|
|
32
|
+
data: payload.data,
|
|
33
|
+
});
|
|
34
|
+
const chunkResults = yield Promise.allSettled(chunk.map((subscription) => __awaiter(this, void 0, void 0, function* () {
|
|
35
|
+
try {
|
|
36
|
+
yield webpush.sendNotification({
|
|
37
|
+
endpoint: subscription.endpoint,
|
|
38
|
+
keys: subscription.keys,
|
|
39
|
+
}, notificationPayload, {
|
|
40
|
+
TTL: 86400, // 24 hours
|
|
41
|
+
urgency: 'normal',
|
|
42
|
+
});
|
|
43
|
+
return { success: true, endpoint: subscription.endpoint };
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error(`Web Push send failed for ${subscription.endpoint}:`, error.message);
|
|
47
|
+
return {
|
|
48
|
+
success: false,
|
|
49
|
+
endpoint: subscription.endpoint,
|
|
50
|
+
error: error.message || 'Unknown error'
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
})));
|
|
54
|
+
for (const result of chunkResults) {
|
|
55
|
+
if (result.status === 'fulfilled') {
|
|
56
|
+
allResults.push(result.value);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
allResults.push({
|
|
60
|
+
success: false,
|
|
61
|
+
endpoint: 'unknown',
|
|
62
|
+
error: ((_a = result.reason) === null || _a === void 0 ? void 0 : _a.message) || 'Promise rejected'
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return allResults;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { WebPushSubscription } from './webpushHelpers';
|
|
2
|
+
type WebPushOrchestratorParams = {
|
|
3
|
+
subscriptions: WebPushSubscription[];
|
|
4
|
+
finalText: string;
|
|
5
|
+
icon?: string;
|
|
6
|
+
link?: string;
|
|
7
|
+
data?: Record<string, unknown>;
|
|
8
|
+
};
|
|
9
|
+
export declare function webpushNotificationBatchOrchestrator(params: WebPushOrchestratorParams): Promise<{
|
|
10
|
+
success: boolean;
|
|
11
|
+
endpoint: string;
|
|
12
|
+
error?: string;
|
|
13
|
+
}[]>;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=webpushNotificationOrchestrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webpushNotificationOrchestrator.d.ts","sourceRoot":"","sources":["../../../src/services/webpush/webpushNotificationOrchestrator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD,KAAK,yBAAyB,GAAG;IAC/B,aAAa,EAAE,mBAAmB,EAAE,CAAC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AAEF,wBAAsB,oCAAoC,CAAC,MAAM,EAAE,yBAAyB;;;;KAc3F"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.webpushNotificationBatchOrchestrator = webpushNotificationBatchOrchestrator;
|
|
13
|
+
const webpushNotification_1 = require("./webpushNotification");
|
|
14
|
+
function webpushNotificationBatchOrchestrator(params) {
|
|
15
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
16
|
+
const { subscriptions, finalText, icon, link, data } = params;
|
|
17
|
+
const payload = {
|
|
18
|
+
title: finalText,
|
|
19
|
+
body: '',
|
|
20
|
+
icon,
|
|
21
|
+
link,
|
|
22
|
+
data,
|
|
23
|
+
};
|
|
24
|
+
console.log(`[webpushNotificationBatchOrchestrator] Sending to ${subscriptions.length} subscriptions`);
|
|
25
|
+
return yield (0, webpushNotification_1.sendWebPushNotificationBatch)(subscriptions, payload);
|
|
26
|
+
});
|
|
27
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "podverse-notifications",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "5.1.19-alpha.0",
|
|
4
4
|
"description": "Push notification helper module for Podverse use cases",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -17,14 +17,16 @@
|
|
|
17
17
|
"license": "AGPLv3",
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"module-alias": "^2.2.3",
|
|
20
|
-
"podverse-external-services": "^5.1.
|
|
21
|
-
"podverse-helpers": "^5.1.
|
|
20
|
+
"podverse-external-services": "^5.1.19-alpha.0",
|
|
21
|
+
"podverse-helpers": "^5.1.19-alpha.0",
|
|
22
|
+
"web-push": "^3.6.7"
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"@eslint/config-array": "^0.21.0",
|
|
25
26
|
"@eslint/eslintrc": "^3.3.1",
|
|
26
27
|
"@eslint/js": "^9.35.0",
|
|
27
28
|
"@types/node": "^24.4.0",
|
|
29
|
+
"@types/web-push": "^3.6.4",
|
|
28
30
|
"eslint": "^9.35.0",
|
|
29
31
|
"nodemon": "^3.1.10",
|
|
30
32
|
"ts-node": "^10.9.2",
|