sitepong 0.1.12 → 0.1.13
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 +21 -17
- package/dist/entries/rn.js +16 -85
- package/dist/entries/rn.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -602,7 +602,7 @@ Native APNs (iOS) and FCM (Android) push notifications, plus iOS Live Activity u
|
|
|
602
602
|
|
|
603
603
|
#### Token lifecycle
|
|
604
604
|
|
|
605
|
-
There are two halves to push: the **client SDK** registers device tokens with the SitePong ingest server, and the **server-side Push API** sends notifications to
|
|
605
|
+
There are two halves to push: the **client SDK** registers device tokens with the SitePong ingest server, and the **server-side Push API** sends notifications and binds tokens to authenticated users.
|
|
606
606
|
|
|
607
607
|
```
|
|
608
608
|
[App launches]
|
|
@@ -611,15 +611,19 @@ There are two halves to push: the **client SDK** registers device tokens with th
|
|
|
611
611
|
↓
|
|
612
612
|
[registerDeviceToken(token, opts)] ← SDK POSTs to /api/push/tokens
|
|
613
613
|
↓ with X-API-Key: sp_live_xxx
|
|
614
|
-
[Token stored in SitePong]
|
|
614
|
+
[Token stored anonymously in SitePong]
|
|
615
615
|
↓
|
|
616
|
-
[
|
|
617
|
-
↓
|
|
618
|
-
[Your backend → POST /api/push/
|
|
616
|
+
[User logs in with your backend]
|
|
617
|
+
↓
|
|
618
|
+
[Your backend → POST /api/push/tokens/associate] ← sp_push_xxx + { token, user_id }
|
|
619
|
+
↓
|
|
620
|
+
[Your backend → POST /api/push/send] ← sp_push_xxx + target user_ids
|
|
619
621
|
↓
|
|
620
622
|
[SitePong → APNs / FCM → Device]
|
|
621
623
|
```
|
|
622
624
|
|
|
625
|
+
> **Why two steps?** Publishable SDK keys (`sp_live_xxx`) are embedded in your app binary and can be extracted by anyone who decompiles the APK/IPA. If the client controlled `user_id`, a leaked SDK key would let an attacker register their own device under your users' IDs and silently receive their push notifications (password resets, OTP codes, order details). User attribution therefore only happens server-to-server with the privileged `sp_push_xxx` key — which stays on your backend.
|
|
626
|
+
|
|
623
627
|
#### 1. Install permission and get the native token
|
|
624
628
|
|
|
625
629
|
```bash
|
|
@@ -649,26 +653,25 @@ import { Platform } from 'react-native';
|
|
|
649
653
|
registerDeviceToken(token, {
|
|
650
654
|
platform: Platform.OS as 'ios' | 'android',
|
|
651
655
|
environment: __DEV__ ? 'sandbox' : 'production',
|
|
652
|
-
userId: 'user-123', // optional — can be set later via identify()
|
|
653
656
|
});
|
|
654
657
|
```
|
|
655
658
|
|
|
656
|
-
This makes a `POST /api/push/tokens` request to the ingest server with `X-API-Key: <your SDK key>`. The body includes the token, `token_type` (`apns`/`fcm`), `device_id`, `
|
|
659
|
+
This makes a `POST /api/push/tokens` request to the ingest server with `X-API-Key: <your SDK key>`. The body includes the token, `token_type` (`apns`/`fcm`), `device_id`, `platform`, app version, device model, and OS version — no `user_id`. The server upserts by `(project_id, token)` so re-registration is idempotent.
|
|
657
660
|
|
|
658
|
-
#### 3.
|
|
661
|
+
#### 3. Associate the token with a user (server-side)
|
|
659
662
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
```typescript
|
|
663
|
-
import { identify } from '@sitepong/sdk/react-native';
|
|
663
|
+
Once your own backend has authenticated the user, have it POST to SitePong's associate endpoint with your `sp_push_xxx` key. Your app should forward the push token to your backend as part of the login flow.
|
|
664
664
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
665
|
+
```bash
|
|
666
|
+
curl -X POST https://api.sitepong.com/api/push/tokens/associate \
|
|
667
|
+
-H "Authorization: Bearer sp_push_xxxxxxxxxxxxxxxx" \
|
|
668
|
+
-H "Content-Type: application/json" \
|
|
669
|
+
-d '{ "token": "<expo_or_native_token>", "user_id": "user-123" }'
|
|
669
670
|
```
|
|
670
671
|
|
|
671
|
-
|
|
672
|
+
On sign-out, POST the same endpoint with `"user_id": null` to clear the association. Because this call is server-authenticated, an attacker who has scraped the SDK key out of your app cannot impersonate a user.
|
|
673
|
+
|
|
674
|
+
> The older pattern of passing `userId` to `registerDeviceToken()` or `pushUserId` to `SitePongRNProvider` still type-checks for backwards compatibility, but the values are silently ignored on both the client and the server. Migrate to the associate endpoint.
|
|
672
675
|
|
|
673
676
|
#### 4. Send notifications from your backend
|
|
674
677
|
|
|
@@ -794,6 +797,7 @@ When APNs or FCM reports a token as invalid (app uninstalled, notifications disa
|
|
|
794
797
|
| POST | `/api/push/live-activity-tokens` | `X-API-Key: sp_live_*` | `registerLiveActivityToken()` |
|
|
795
798
|
| POST | `/api/push/push-to-start-tokens` | `X-API-Key: sp_live_*` | `registerPushToStartToken()` |
|
|
796
799
|
| DELETE | `/api/push/live-activity-tokens` | `X-API-Key: sp_live_*` | `endLiveActivity()` |
|
|
800
|
+
| POST | `/api/push/tokens/associate` | `Authorization: Bearer sp_push_*` | (server-side only) |
|
|
797
801
|
| POST | `/api/push/send` | `Authorization: Bearer sp_push_*` | (server-side only) |
|
|
798
802
|
| POST | `/api/push/live-activity` | `Authorization: Bearer sp_push_*` | (server-side only) |
|
|
799
803
|
|
package/dist/entries/rn.js
CHANGED
|
@@ -3163,22 +3163,7 @@ var RNPerformanceManager = class {
|
|
|
3163
3163
|
};
|
|
3164
3164
|
|
|
3165
3165
|
// src/react-native/push.ts
|
|
3166
|
-
var identifyHookInstalled = false;
|
|
3167
|
-
function ensureIdentifyHookInstalled() {
|
|
3168
|
-
if (identifyHookInstalled) return;
|
|
3169
|
-
identifyHookInstalled = true;
|
|
3170
|
-
try {
|
|
3171
|
-
const c = sitepong;
|
|
3172
|
-
if (typeof c.registerIdentifyHook === "function") {
|
|
3173
|
-
c.registerIdentifyHook(reRegisterTokensWithUserId);
|
|
3174
|
-
}
|
|
3175
|
-
} catch {
|
|
3176
|
-
}
|
|
3177
|
-
}
|
|
3178
3166
|
var cachedDeviceContext = null;
|
|
3179
|
-
var registeredPushToken = null;
|
|
3180
|
-
var registeredLiveActivityTokens = /* @__PURE__ */ new Map();
|
|
3181
|
-
var registeredPushToStartTokens = /* @__PURE__ */ new Map();
|
|
3182
3167
|
function getEndpoint() {
|
|
3183
3168
|
const cfg = sitepong.config;
|
|
3184
3169
|
return cfg?.endpoint || "https://ingest.sitepong.com";
|
|
@@ -3205,14 +3190,6 @@ function getDeviceContext() {
|
|
|
3205
3190
|
}
|
|
3206
3191
|
return cachedDeviceContext;
|
|
3207
3192
|
}
|
|
3208
|
-
function getUserId() {
|
|
3209
|
-
try {
|
|
3210
|
-
const analyticsManager = sitepong.analyticsManager;
|
|
3211
|
-
return analyticsManager?.getUserId?.() || void 0;
|
|
3212
|
-
} catch {
|
|
3213
|
-
return void 0;
|
|
3214
|
-
}
|
|
3215
|
-
}
|
|
3216
3193
|
async function postToIngest(path, body) {
|
|
3217
3194
|
const endpoint = getEndpoint();
|
|
3218
3195
|
const apiKey = getApiKey();
|
|
@@ -3256,17 +3233,26 @@ async function deleteFromIngest(path, body) {
|
|
|
3256
3233
|
console.warn("[SitePong] Push token deactivation failed:", err);
|
|
3257
3234
|
}
|
|
3258
3235
|
}
|
|
3236
|
+
function registerPushToken(token, options) {
|
|
3237
|
+
const device = getDeviceContext();
|
|
3238
|
+
postToIngest("/api/push/tokens", {
|
|
3239
|
+
expo_push_token: token,
|
|
3240
|
+
environment: options.environment,
|
|
3241
|
+
device_id: device.deviceId,
|
|
3242
|
+
platform: device.platform,
|
|
3243
|
+
app_version: device.appVersion,
|
|
3244
|
+
device_model: device.deviceModel,
|
|
3245
|
+
os_version: device.osVersion
|
|
3246
|
+
});
|
|
3247
|
+
}
|
|
3259
3248
|
function registerDeviceToken(token, options) {
|
|
3260
|
-
ensureIdentifyHookInstalled();
|
|
3261
3249
|
const device = getDeviceContext();
|
|
3262
3250
|
const tokenType = options.platform === "ios" ? "apns" : "fcm";
|
|
3263
|
-
registeredPushToken = { token, environment: options.environment };
|
|
3264
3251
|
postToIngest("/api/push/tokens", {
|
|
3265
3252
|
native_device_token: token,
|
|
3266
3253
|
token_type: tokenType,
|
|
3267
3254
|
environment: options.environment,
|
|
3268
3255
|
device_id: device.deviceId,
|
|
3269
|
-
user_id: options.userId || void 0,
|
|
3270
3256
|
platform: options.platform,
|
|
3271
3257
|
app_version: device.appVersion,
|
|
3272
3258
|
device_model: device.deviceModel,
|
|
@@ -3274,86 +3260,30 @@ function registerDeviceToken(token, options) {
|
|
|
3274
3260
|
});
|
|
3275
3261
|
}
|
|
3276
3262
|
function registerLiveActivityToken(activityType, activityId, pushToken, options) {
|
|
3277
|
-
ensureIdentifyHookInstalled();
|
|
3278
3263
|
const device = getDeviceContext();
|
|
3279
|
-
const userId = getUserId();
|
|
3280
|
-
const key = `${activityType}:${activityId}`;
|
|
3281
|
-
registeredLiveActivityTokens.set(key, {
|
|
3282
|
-
activityType,
|
|
3283
|
-
activityId,
|
|
3284
|
-
token: pushToken,
|
|
3285
|
-
environment: options.environment
|
|
3286
|
-
});
|
|
3287
3264
|
postToIngest("/api/push/live-activity-tokens", {
|
|
3288
3265
|
activity_type: activityType,
|
|
3289
3266
|
activity_id: activityId,
|
|
3290
3267
|
push_token: pushToken,
|
|
3291
3268
|
environment: options.environment,
|
|
3292
|
-
device_id: device.deviceId
|
|
3293
|
-
user_id: userId
|
|
3269
|
+
device_id: device.deviceId
|
|
3294
3270
|
});
|
|
3295
3271
|
}
|
|
3296
3272
|
function registerPushToStartToken(activityType, pushToStartToken, options) {
|
|
3297
|
-
ensureIdentifyHookInstalled();
|
|
3298
3273
|
const device = getDeviceContext();
|
|
3299
|
-
const userId = getUserId();
|
|
3300
|
-
registeredPushToStartTokens.set(activityType, {
|
|
3301
|
-
activityType,
|
|
3302
|
-
token: pushToStartToken,
|
|
3303
|
-
environment: options.environment
|
|
3304
|
-
});
|
|
3305
3274
|
postToIngest("/api/push/push-to-start-tokens", {
|
|
3306
3275
|
activity_type: activityType,
|
|
3307
3276
|
push_to_start_token: pushToStartToken,
|
|
3308
3277
|
environment: options.environment,
|
|
3309
|
-
device_id: device.deviceId
|
|
3310
|
-
user_id: userId
|
|
3278
|
+
device_id: device.deviceId
|
|
3311
3279
|
});
|
|
3312
3280
|
}
|
|
3313
3281
|
function endLiveActivity(activityType, activityId) {
|
|
3314
|
-
const key = `${activityType}:${activityId}`;
|
|
3315
|
-
registeredLiveActivityTokens.delete(key);
|
|
3316
3282
|
deleteFromIngest("/api/push/live-activity-tokens", {
|
|
3317
3283
|
activity_type: activityType,
|
|
3318
3284
|
activity_id: activityId
|
|
3319
3285
|
});
|
|
3320
3286
|
}
|
|
3321
|
-
function reRegisterTokensWithUserId(userId) {
|
|
3322
|
-
if (registeredPushToken) {
|
|
3323
|
-
const device = getDeviceContext();
|
|
3324
|
-
postToIngest("/api/push/tokens", {
|
|
3325
|
-
expo_push_token: registeredPushToken.token,
|
|
3326
|
-
environment: registeredPushToken.environment,
|
|
3327
|
-
device_id: device.deviceId,
|
|
3328
|
-
user_id: userId,
|
|
3329
|
-
platform: device.platform,
|
|
3330
|
-
app_version: device.appVersion,
|
|
3331
|
-
device_model: device.deviceModel,
|
|
3332
|
-
os_version: device.osVersion
|
|
3333
|
-
});
|
|
3334
|
-
}
|
|
3335
|
-
for (const entry of registeredLiveActivityTokens.values()) {
|
|
3336
|
-
const device = getDeviceContext();
|
|
3337
|
-
postToIngest("/api/push/live-activity-tokens", {
|
|
3338
|
-
activity_type: entry.activityType,
|
|
3339
|
-
activity_id: entry.activityId,
|
|
3340
|
-
push_token: entry.token,
|
|
3341
|
-
environment: entry.environment,
|
|
3342
|
-
device_id: device.deviceId,
|
|
3343
|
-
user_id: userId
|
|
3344
|
-
});
|
|
3345
|
-
}
|
|
3346
|
-
for (const entry of registeredPushToStartTokens.values()) {
|
|
3347
|
-
const device = getDeviceContext();
|
|
3348
|
-
postToIngest("/api/push/push-to-start-tokens", {
|
|
3349
|
-
activity_type: entry.activityType,
|
|
3350
|
-
push_to_start_token: entry.token,
|
|
3351
|
-
environment: entry.environment,
|
|
3352
|
-
device_id: device.deviceId,
|
|
3353
|
-
user_id: userId
|
|
3354
|
-
});
|
|
3355
|
-
}
|
|
3356
|
-
}
|
|
3357
3287
|
var SitePongRNContext = react.createContext({
|
|
3358
3288
|
isInitialized: false,
|
|
3359
3289
|
performanceManager: null
|
|
@@ -3426,7 +3356,7 @@ function SitePongRNProvider({
|
|
|
3426
3356
|
teardowns.push(() => stopScreenRecording());
|
|
3427
3357
|
}
|
|
3428
3358
|
if (devicePushToken && devicePlatform) {
|
|
3429
|
-
registerDeviceToken(devicePushToken, { platform: devicePlatform, environment: pushEnvironment
|
|
3359
|
+
registerDeviceToken(devicePushToken, { platform: devicePlatform, environment: pushEnvironment});
|
|
3430
3360
|
}
|
|
3431
3361
|
performanceManagerRef.current = new RNPerformanceManager();
|
|
3432
3362
|
performanceManagerRef.current.trackColdStart();
|
|
@@ -3646,6 +3576,7 @@ exports.refreshFlags = refreshFlags;
|
|
|
3646
3576
|
exports.registerDeviceToken = registerDeviceToken;
|
|
3647
3577
|
exports.registerLiveActivityToken = registerLiveActivityToken;
|
|
3648
3578
|
exports.registerPushToStartToken = registerPushToStartToken;
|
|
3579
|
+
exports.registerPushToken = registerPushToken;
|
|
3649
3580
|
exports.resetAnalytics = resetAnalytics;
|
|
3650
3581
|
exports.setAnonymousId = setAnonymousId;
|
|
3651
3582
|
exports.setContext = setContext;
|