@oxyhq/core 1.5.0 → 1.6.1
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/dist/cjs/AuthManager.js +14 -1
- package/dist/cjs/HttpService.js +87 -69
- package/dist/cjs/OxyServices.base.js +5 -4
- package/dist/cjs/crypto/keyManager.js +1 -13
- package/dist/cjs/crypto/signatureService.js +7 -20
- package/dist/cjs/index.js +9 -1
- package/dist/cjs/mixins/OxyServices.analytics.js +2 -2
- package/dist/cjs/mixins/OxyServices.assets.js +14 -14
- package/dist/cjs/mixins/OxyServices.auth.js +19 -19
- package/dist/cjs/mixins/OxyServices.developer.js +6 -6
- package/dist/cjs/mixins/OxyServices.devices.js +7 -7
- package/dist/cjs/mixins/OxyServices.features.js +23 -23
- package/dist/cjs/mixins/OxyServices.fedcm.js +1 -1
- package/dist/cjs/mixins/OxyServices.karma.js +6 -6
- package/dist/cjs/mixins/OxyServices.location.js +2 -2
- package/dist/cjs/mixins/OxyServices.payment.js +6 -6
- package/dist/cjs/mixins/OxyServices.popup.js +1 -1
- package/dist/cjs/mixins/OxyServices.privacy.js +6 -6
- package/dist/cjs/mixins/OxyServices.security.js +3 -3
- package/dist/cjs/mixins/OxyServices.user.js +22 -22
- package/dist/cjs/mixins/OxyServices.utility.js +39 -10
- package/dist/cjs/utils/authHelpers.js +114 -0
- package/dist/cjs/utils/platform.js +14 -0
- package/dist/esm/AuthManager.js +14 -1
- package/dist/esm/HttpService.js +87 -69
- package/dist/esm/OxyServices.base.js +5 -4
- package/dist/esm/crypto/keyManager.js +1 -13
- package/dist/esm/crypto/signatureService.js +2 -15
- package/dist/esm/index.js +2 -0
- package/dist/esm/mixins/OxyServices.analytics.js +2 -2
- package/dist/esm/mixins/OxyServices.assets.js +14 -14
- package/dist/esm/mixins/OxyServices.auth.js +19 -19
- package/dist/esm/mixins/OxyServices.developer.js +6 -6
- package/dist/esm/mixins/OxyServices.devices.js +7 -7
- package/dist/esm/mixins/OxyServices.features.js +23 -23
- package/dist/esm/mixins/OxyServices.fedcm.js +1 -1
- package/dist/esm/mixins/OxyServices.karma.js +6 -6
- package/dist/esm/mixins/OxyServices.location.js +2 -2
- package/dist/esm/mixins/OxyServices.payment.js +6 -6
- package/dist/esm/mixins/OxyServices.popup.js +1 -1
- package/dist/esm/mixins/OxyServices.privacy.js +6 -6
- package/dist/esm/mixins/OxyServices.security.js +3 -3
- package/dist/esm/mixins/OxyServices.user.js +22 -22
- package/dist/esm/mixins/OxyServices.utility.js +39 -10
- package/dist/esm/utils/authHelpers.js +105 -0
- package/dist/esm/utils/platform.js +12 -0
- package/dist/types/HttpService.d.ts +4 -1
- package/dist/types/OxyServices.base.d.ts +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/mixins/OxyServices.analytics.d.ts +1 -1
- package/dist/types/mixins/OxyServices.assets.d.ts +1 -1
- package/dist/types/mixins/OxyServices.auth.d.ts +1 -1
- package/dist/types/mixins/OxyServices.developer.d.ts +1 -1
- package/dist/types/mixins/OxyServices.devices.d.ts +1 -1
- package/dist/types/mixins/OxyServices.features.d.ts +1 -1
- package/dist/types/mixins/OxyServices.fedcm.d.ts +1 -1
- package/dist/types/mixins/OxyServices.karma.d.ts +1 -1
- package/dist/types/mixins/OxyServices.language.d.ts +1 -1
- package/dist/types/mixins/OxyServices.location.d.ts +1 -1
- package/dist/types/mixins/OxyServices.payment.d.ts +1 -1
- package/dist/types/mixins/OxyServices.popup.d.ts +1 -1
- package/dist/types/mixins/OxyServices.privacy.d.ts +1 -1
- package/dist/types/mixins/OxyServices.redirect.d.ts +1 -1
- package/dist/types/mixins/OxyServices.security.d.ts +1 -1
- package/dist/types/mixins/OxyServices.user.d.ts +1 -1
- package/dist/types/mixins/OxyServices.utility.d.ts +20 -6
- package/dist/types/utils/authHelpers.d.ts +57 -0
- package/dist/types/utils/platform.d.ts +8 -0
- package/package.json +1 -1
- package/src/AuthManager.ts +14 -1
- package/src/HttpService.ts +85 -67
- package/src/OxyServices.base.ts +5 -4
- package/src/crypto/keyManager.ts +1 -15
- package/src/crypto/signatureService.ts +2 -17
- package/src/index.ts +11 -0
- package/src/mixins/OxyServices.analytics.ts +2 -2
- package/src/mixins/OxyServices.assets.ts +14 -14
- package/src/mixins/OxyServices.auth.ts +19 -19
- package/src/mixins/OxyServices.developer.ts +6 -6
- package/src/mixins/OxyServices.devices.ts +7 -7
- package/src/mixins/OxyServices.features.ts +23 -23
- package/src/mixins/OxyServices.fedcm.ts +1 -1
- package/src/mixins/OxyServices.karma.ts +6 -6
- package/src/mixins/OxyServices.location.ts +2 -2
- package/src/mixins/OxyServices.payment.ts +6 -6
- package/src/mixins/OxyServices.popup.ts +1 -1
- package/src/mixins/OxyServices.privacy.ts +6 -6
- package/src/mixins/OxyServices.security.ts +3 -3
- package/src/mixins/OxyServices.user.ts +22 -22
- package/src/mixins/OxyServices.utility.ts +41 -11
- package/src/utils/authHelpers.ts +140 -0
- package/src/utils/platform.ts +14 -0
package/dist/esm/HttpService.js
CHANGED
|
@@ -25,7 +25,9 @@ import { isNative, getPlatformOS } from './utils/platform';
|
|
|
25
25
|
*/
|
|
26
26
|
const isNativeApp = isNative();
|
|
27
27
|
/**
|
|
28
|
-
* Token store for authentication (
|
|
28
|
+
* Token store for authentication (instance-based)
|
|
29
|
+
* Each HttpService gets its own TokenStore to prevent conflicts
|
|
30
|
+
* when multiple OxyServices instances coexist server-side.
|
|
29
31
|
*/
|
|
30
32
|
class TokenStore {
|
|
31
33
|
constructor() {
|
|
@@ -34,12 +36,6 @@ class TokenStore {
|
|
|
34
36
|
this.csrfToken = null;
|
|
35
37
|
this.csrfTokenFetchPromise = null;
|
|
36
38
|
}
|
|
37
|
-
static getInstance() {
|
|
38
|
-
if (!TokenStore.instance) {
|
|
39
|
-
TokenStore.instance = new TokenStore();
|
|
40
|
-
}
|
|
41
|
-
return TokenStore.instance;
|
|
42
|
-
}
|
|
43
39
|
setTokens(accessToken, refreshToken = '') {
|
|
44
40
|
this.accessToken = accessToken;
|
|
45
41
|
this.refreshToken = refreshToken;
|
|
@@ -83,6 +79,7 @@ class TokenStore {
|
|
|
83
79
|
export class HttpService {
|
|
84
80
|
constructor(config) {
|
|
85
81
|
this.tokenRefreshPromise = null;
|
|
82
|
+
this.tokenRefreshCooldownUntil = 0;
|
|
86
83
|
this._onTokenRefreshed = null;
|
|
87
84
|
// Performance monitoring
|
|
88
85
|
this.requestMetrics = {
|
|
@@ -95,7 +92,7 @@ export class HttpService {
|
|
|
95
92
|
};
|
|
96
93
|
this.config = config;
|
|
97
94
|
this.baseURL = config.baseURL;
|
|
98
|
-
this.tokenStore = TokenStore
|
|
95
|
+
this.tokenStore = new TokenStore();
|
|
99
96
|
this.logger = new SimpleLogger(config.enableLogging || false, config.logLevel || 'error', 'HttpService');
|
|
100
97
|
// Initialize performance infrastructure
|
|
101
98
|
this.cache = new TTLCache(config.cacheTTL || 5 * 60 * 1000);
|
|
@@ -247,6 +244,20 @@ export class HttpService {
|
|
|
247
244
|
// Refresh failed or no token — clear tokens
|
|
248
245
|
this.tokenStore.clearTokens();
|
|
249
246
|
}
|
|
247
|
+
// On 403 with CSRF error, clear cached token and retry once
|
|
248
|
+
if (response.status === 403 && !config._isCsrfRetry) {
|
|
249
|
+
try {
|
|
250
|
+
const clonedResponse = response.clone();
|
|
251
|
+
const errBody = await clonedResponse.json();
|
|
252
|
+
if (errBody?.code === 'CSRF_TOKEN_INVALID' || errBody?.code === 'CSRF_TOKEN_MISSING') {
|
|
253
|
+
this.tokenStore.clearCsrfToken();
|
|
254
|
+
return this.request({ ...config, _isCsrfRetry: true, retry: false });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
// Failed to parse error body — not a CSRF error
|
|
259
|
+
}
|
|
260
|
+
}
|
|
250
261
|
// Try to parse error response (handle empty/malformed JSON)
|
|
251
262
|
let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
|
|
252
263
|
const contentType = response.headers.get('content-type');
|
|
@@ -403,52 +414,57 @@ export class HttpService {
|
|
|
403
414
|
return existingPromise;
|
|
404
415
|
}
|
|
405
416
|
const fetchPromise = (async () => {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
417
|
+
const maxAttempts = 2;
|
|
418
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
419
|
+
try {
|
|
420
|
+
if (isDev())
|
|
421
|
+
console.log('[HttpService] Fetching CSRF token from:', `${this.baseURL}/csrf-token`, `(attempt ${attempt})`);
|
|
422
|
+
// Use AbortController for timeout (more compatible than AbortSignal.timeout)
|
|
423
|
+
const controller = new AbortController();
|
|
424
|
+
const timeoutId = setTimeout(() => controller.abort(), 5000);
|
|
425
|
+
const response = await fetch(`${this.baseURL}/csrf-token`, {
|
|
426
|
+
method: 'GET',
|
|
427
|
+
headers: { 'Accept': 'application/json' },
|
|
428
|
+
credentials: 'include', // Required to receive and send cookies
|
|
429
|
+
signal: controller.signal,
|
|
430
|
+
});
|
|
431
|
+
clearTimeout(timeoutId);
|
|
432
|
+
if (isDev())
|
|
433
|
+
console.log('[HttpService] CSRF fetch response:', response.status, response.ok);
|
|
434
|
+
if (response.ok) {
|
|
435
|
+
const data = await response.json();
|
|
436
|
+
if (isDev())
|
|
437
|
+
console.log('[HttpService] CSRF response data:', data);
|
|
438
|
+
const token = data.csrfToken || null;
|
|
439
|
+
this.tokenStore.setCsrfToken(token);
|
|
440
|
+
this.logger.debug('CSRF token fetched');
|
|
441
|
+
return token;
|
|
442
|
+
}
|
|
443
|
+
// Also check response header for CSRF token
|
|
444
|
+
const headerToken = response.headers.get('X-CSRF-Token');
|
|
445
|
+
if (headerToken) {
|
|
446
|
+
this.tokenStore.setCsrfToken(headerToken);
|
|
447
|
+
this.logger.debug('CSRF token from header');
|
|
448
|
+
return headerToken;
|
|
449
|
+
}
|
|
423
450
|
if (isDev())
|
|
424
|
-
console.log('[HttpService] CSRF
|
|
425
|
-
|
|
426
|
-
this.tokenStore.setCsrfToken(token);
|
|
427
|
-
this.logger.debug('CSRF token fetched');
|
|
428
|
-
return token;
|
|
451
|
+
console.log('[HttpService] CSRF fetch failed with status:', response.status);
|
|
452
|
+
this.logger.warn('Failed to fetch CSRF token:', response.status);
|
|
429
453
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
this.
|
|
434
|
-
|
|
435
|
-
|
|
454
|
+
catch (error) {
|
|
455
|
+
if (isDev())
|
|
456
|
+
console.log('[HttpService] CSRF fetch error:', error);
|
|
457
|
+
this.logger.warn('CSRF token fetch error:', error);
|
|
458
|
+
}
|
|
459
|
+
// Wait before retry (500ms)
|
|
460
|
+
if (attempt < maxAttempts) {
|
|
461
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
436
462
|
}
|
|
437
|
-
if (isDev())
|
|
438
|
-
console.log('[HttpService] CSRF fetch failed with status:', response.status);
|
|
439
|
-
this.logger.warn('Failed to fetch CSRF token:', response.status);
|
|
440
|
-
return null;
|
|
441
|
-
}
|
|
442
|
-
catch (error) {
|
|
443
|
-
if (isDev())
|
|
444
|
-
console.log('[HttpService] CSRF fetch error:', error);
|
|
445
|
-
this.logger.warn('CSRF token fetch error:', error);
|
|
446
|
-
return null;
|
|
447
|
-
}
|
|
448
|
-
finally {
|
|
449
|
-
this.tokenStore.setCsrfTokenFetchPromise(null);
|
|
450
463
|
}
|
|
451
|
-
|
|
464
|
+
return null;
|
|
465
|
+
})().finally(() => {
|
|
466
|
+
this.tokenStore.setCsrfTokenFetchPromise(null);
|
|
467
|
+
});
|
|
452
468
|
this.tokenStore.setCsrfTokenFetchPromise(fetchPromise);
|
|
453
469
|
return fetchPromise;
|
|
454
470
|
}
|
|
@@ -465,18 +481,25 @@ export class HttpService {
|
|
|
465
481
|
const currentTime = Math.floor(Date.now() / 1000);
|
|
466
482
|
// If token expires in less than 60 seconds, refresh it
|
|
467
483
|
if (decoded.exp && decoded.exp - currentTime < 60 && decoded.sessionId) {
|
|
468
|
-
//
|
|
469
|
-
if (
|
|
470
|
-
|
|
484
|
+
// Skip if we recently failed a refresh (5s cooldown to prevent storms)
|
|
485
|
+
if (Date.now() < this.tokenRefreshCooldownUntil) {
|
|
486
|
+
return `Bearer ${accessToken}`;
|
|
471
487
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
488
|
+
// Deduplicate concurrent refresh attempts. The promise is shared
|
|
489
|
+
// across all concurrent callers and cleared only after it settles,
|
|
490
|
+
// so every awaiter receives the same result.
|
|
491
|
+
if (!this.tokenRefreshPromise) {
|
|
492
|
+
this.tokenRefreshPromise = this._refreshTokenFromSession(decoded.sessionId)
|
|
493
|
+
.then((result) => {
|
|
494
|
+
if (!result)
|
|
495
|
+
this.tokenRefreshCooldownUntil = Date.now() + 5000;
|
|
475
496
|
return result;
|
|
497
|
+
})
|
|
498
|
+
.finally(() => { this.tokenRefreshPromise = null; });
|
|
476
499
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
500
|
+
const result = await this.tokenRefreshPromise;
|
|
501
|
+
if (result)
|
|
502
|
+
return result;
|
|
480
503
|
}
|
|
481
504
|
return `Bearer ${accessToken}`;
|
|
482
505
|
}
|
|
@@ -487,7 +510,7 @@ export class HttpService {
|
|
|
487
510
|
}
|
|
488
511
|
async _refreshTokenFromSession(sessionId) {
|
|
489
512
|
try {
|
|
490
|
-
const refreshUrl = `${this.baseURL}/
|
|
513
|
+
const refreshUrl = `${this.baseURL}/session/token/${sessionId}`;
|
|
491
514
|
const response = await fetch(refreshUrl, {
|
|
492
515
|
method: 'GET',
|
|
493
516
|
headers: { 'Accept': 'application/json' },
|
|
@@ -640,14 +663,9 @@ export class HttpService {
|
|
|
640
663
|
getMetrics() {
|
|
641
664
|
return { ...this.requestMetrics };
|
|
642
665
|
}
|
|
643
|
-
// Test-only utility
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
}
|
|
648
|
-
catch (error) {
|
|
649
|
-
// Silently fail in test cleanup - this is expected behavior
|
|
650
|
-
// TokenStore might not be initialized in some test scenarios
|
|
651
|
-
}
|
|
666
|
+
// Test-only utility — clears tokens on this instance
|
|
667
|
+
__resetTokensForTests() {
|
|
668
|
+
this.tokenStore.clearTokens();
|
|
669
|
+
this.tokenStore.clearCsrfToken();
|
|
652
670
|
}
|
|
653
671
|
}
|
|
@@ -23,9 +23,10 @@ export class OxyServicesBase {
|
|
|
23
23
|
// Initialize unified HTTP service (handles auth, caching, deduplication, queuing, retry)
|
|
24
24
|
this.httpService = new HttpService(config);
|
|
25
25
|
}
|
|
26
|
-
// Test-only utility to reset
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
// Test-only utility to reset tokens on this instance between jest tests
|
|
27
|
+
// Note: tokens are now per-instance, so create new instances in tests for isolation
|
|
28
|
+
__resetTokensForTests() {
|
|
29
|
+
this.httpService.__resetTokensForTests();
|
|
29
30
|
}
|
|
30
31
|
/**
|
|
31
32
|
* Make a request with all performance optimizations
|
|
@@ -231,7 +232,7 @@ export class OxyServicesBase {
|
|
|
231
232
|
return false;
|
|
232
233
|
}
|
|
233
234
|
try {
|
|
234
|
-
const res = await this.makeRequest('GET', '/
|
|
235
|
+
const res = await this.makeRequest('GET', '/auth/validate', undefined, {
|
|
235
236
|
cache: false,
|
|
236
237
|
retry: false,
|
|
237
238
|
});
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Private keys are stored securely using expo-secure-store and never leave the device.
|
|
6
6
|
*/
|
|
7
7
|
import { ec as EC } from 'elliptic';
|
|
8
|
-
import { isWeb, isIOS, isAndroid } from '../utils/platform';
|
|
8
|
+
import { isWeb, isIOS, isAndroid, isReactNative, isNodeJS } from '../utils/platform';
|
|
9
9
|
import { logger } from '../utils/loggerUtils';
|
|
10
10
|
import { isDev } from '../shared/utils/debugUtils';
|
|
11
11
|
// Lazy imports for React Native specific modules
|
|
@@ -56,18 +56,6 @@ async function initSecureStore() {
|
|
|
56
56
|
}
|
|
57
57
|
return SecureStore;
|
|
58
58
|
}
|
|
59
|
-
/**
|
|
60
|
-
* Check if we're in a React Native environment
|
|
61
|
-
*/
|
|
62
|
-
function isReactNative() {
|
|
63
|
-
return typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Check if we're in a Node.js environment
|
|
67
|
-
*/
|
|
68
|
-
function isNodeJS() {
|
|
69
|
-
return typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
|
|
70
|
-
}
|
|
71
59
|
/**
|
|
72
60
|
* Check if we're on web platform
|
|
73
61
|
* Identity storage is only available on native platforms (iOS/Android)
|
|
@@ -6,21 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { ec as EC } from 'elliptic';
|
|
8
8
|
import { KeyManager } from './keyManager';
|
|
9
|
+
import { isReactNative, isNodeJS } from '../utils/platform';
|
|
9
10
|
// Lazy import for expo-crypto
|
|
10
11
|
let ExpoCrypto = null;
|
|
11
12
|
const ec = new EC('secp256k1');
|
|
12
|
-
/**
|
|
13
|
-
* Check if we're in a React Native environment
|
|
14
|
-
*/
|
|
15
|
-
function isReactNative() {
|
|
16
|
-
return typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Check if we're in a Node.js environment
|
|
20
|
-
*/
|
|
21
|
-
function isNodeJS() {
|
|
22
|
-
return typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
|
|
23
|
-
}
|
|
24
13
|
/**
|
|
25
14
|
* Initialize expo-crypto module
|
|
26
15
|
*/
|
|
@@ -44,9 +33,7 @@ async function sha256(message) {
|
|
|
44
33
|
// In Node.js, use Node's crypto module
|
|
45
34
|
if (isNodeJS()) {
|
|
46
35
|
try {
|
|
47
|
-
|
|
48
|
-
const getCrypto = new Function('return require("crypto")');
|
|
49
|
-
const nodeCrypto = getCrypto();
|
|
36
|
+
const nodeCrypto = await import('crypto');
|
|
50
37
|
return nodeCrypto.createHash('sha256').update(message).digest('hex');
|
|
51
38
|
}
|
|
52
39
|
catch {
|
package/dist/esm/index.js
CHANGED
|
@@ -40,6 +40,8 @@ export { DEFAULT_CIRCUIT_BREAKER_CONFIG, createCircuitBreakerState, calculateBac
|
|
|
40
40
|
export { isDev, debugLog, debugWarn, debugError, createDebugLogger, } from './shared/utils/debugUtils';
|
|
41
41
|
// --- i18n ---
|
|
42
42
|
export { translate } from './i18n';
|
|
43
|
+
// --- Auth Helpers ---
|
|
44
|
+
export { SessionSyncRequiredError, AuthenticationFailedError, ensureValidToken, isAuthenticationError, withAuthErrorHandling, authenticatedApiCall, } from './utils/authHelpers';
|
|
43
45
|
// --- Session Utilities ---
|
|
44
46
|
export { mergeSessions, normalizeAndSortSessions, sessionsArraysEqual } from './utils/sessionUtils';
|
|
45
47
|
// --- Constants ---
|
|
@@ -11,7 +11,7 @@ export function OxyServicesAnalyticsMixin(Base) {
|
|
|
11
11
|
*/
|
|
12
12
|
async trackEvent(eventName, properties) {
|
|
13
13
|
try {
|
|
14
|
-
await this.makeRequest('POST', '/
|
|
14
|
+
await this.makeRequest('POST', '/analytics/events', {
|
|
15
15
|
event: eventName,
|
|
16
16
|
properties
|
|
17
17
|
}, { cache: false, retry: false }); // Don't retry analytics events
|
|
@@ -33,7 +33,7 @@ export function OxyServicesAnalyticsMixin(Base) {
|
|
|
33
33
|
params.startDate = startDate;
|
|
34
34
|
if (endDate)
|
|
35
35
|
params.endDate = endDate;
|
|
36
|
-
return await this.makeRequest('GET', '/
|
|
36
|
+
return await this.makeRequest('GET', '/analytics', params, {
|
|
37
37
|
cache: true,
|
|
38
38
|
cacheTTL: CACHE_TIMES.LONG,
|
|
39
39
|
});
|
|
@@ -8,7 +8,7 @@ export function OxyServicesAssetsMixin(Base) {
|
|
|
8
8
|
*/
|
|
9
9
|
async deleteFile(fileId) {
|
|
10
10
|
try {
|
|
11
|
-
return await this.makeRequest('DELETE', `/
|
|
11
|
+
return await this.makeRequest('DELETE', `/assets/${encodeURIComponent(fileId)}`, undefined, { cache: false });
|
|
12
12
|
}
|
|
13
13
|
catch (error) {
|
|
14
14
|
throw this.handleError(error);
|
|
@@ -29,7 +29,7 @@ export function OxyServicesAssetsMixin(Base) {
|
|
|
29
29
|
if (token)
|
|
30
30
|
params.set('token', token);
|
|
31
31
|
const qs = params.toString();
|
|
32
|
-
return `${base}/
|
|
32
|
+
return `${base}/assets/${encodeURIComponent(fileId)}/stream${qs ? `?${qs}` : ''}`;
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
35
|
* Get file download URL asynchronously (returns signed URL directly from CDN)
|
|
@@ -53,7 +53,7 @@ export function OxyServicesAssetsMixin(Base) {
|
|
|
53
53
|
paramsObj.limit = limit;
|
|
54
54
|
if (offset)
|
|
55
55
|
paramsObj.offset = offset;
|
|
56
|
-
return await this.makeRequest('GET', '/
|
|
56
|
+
return await this.makeRequest('GET', '/assets', paramsObj, {
|
|
57
57
|
cache: false,
|
|
58
58
|
});
|
|
59
59
|
}
|
|
@@ -66,7 +66,7 @@ export function OxyServicesAssetsMixin(Base) {
|
|
|
66
66
|
*/
|
|
67
67
|
async getAccountStorageUsage() {
|
|
68
68
|
try {
|
|
69
|
-
return await this.makeRequest('GET', '/
|
|
69
|
+
return await this.makeRequest('GET', '/storage/usage', undefined, {
|
|
70
70
|
cache: false,
|
|
71
71
|
});
|
|
72
72
|
}
|
|
@@ -109,7 +109,7 @@ export function OxyServicesAssetsMixin(Base) {
|
|
|
109
109
|
*/
|
|
110
110
|
async getBatchFileAccess(fileIds, context) {
|
|
111
111
|
try {
|
|
112
|
-
return await this.makeRequest('POST', '/
|
|
112
|
+
return await this.makeRequest('POST', '/assets/batch-access', {
|
|
113
113
|
fileIds,
|
|
114
114
|
context
|
|
115
115
|
});
|
|
@@ -170,7 +170,7 @@ export function OxyServicesAssetsMixin(Base) {
|
|
|
170
170
|
}
|
|
171
171
|
const response = await this.getClient().request({
|
|
172
172
|
method: 'POST',
|
|
173
|
-
url: '/
|
|
173
|
+
url: '/assets/upload',
|
|
174
174
|
data: formData,
|
|
175
175
|
cache: false,
|
|
176
176
|
});
|
|
@@ -228,7 +228,7 @@ export function OxyServicesAssetsMixin(Base) {
|
|
|
228
228
|
body.visibility = visibility;
|
|
229
229
|
if (webhookUrl)
|
|
230
230
|
body.webhookUrl = webhookUrl;
|
|
231
|
-
return await this.makeRequest('POST', `/
|
|
231
|
+
return await this.makeRequest('POST', `/assets/${fileId}/links`, body, { cache: false });
|
|
232
232
|
}
|
|
233
233
|
catch (error) {
|
|
234
234
|
throw this.handleError(error);
|
|
@@ -239,7 +239,7 @@ export function OxyServicesAssetsMixin(Base) {
|
|
|
239
239
|
*/
|
|
240
240
|
async assetUnlink(fileId, app, entityType, entityId) {
|
|
241
241
|
try {
|
|
242
|
-
return await this.makeRequest('DELETE', `/
|
|
242
|
+
return await this.makeRequest('DELETE', `/assets/${fileId}/links`, {
|
|
243
243
|
app,
|
|
244
244
|
entityType,
|
|
245
245
|
entityId
|
|
@@ -254,7 +254,7 @@ export function OxyServicesAssetsMixin(Base) {
|
|
|
254
254
|
*/
|
|
255
255
|
async assetGet(fileId) {
|
|
256
256
|
try {
|
|
257
|
-
return await this.makeRequest('GET', `/
|
|
257
|
+
return await this.makeRequest('GET', `/assets/${fileId}`, undefined, {
|
|
258
258
|
cache: true,
|
|
259
259
|
cacheTTL: 5 * 60 * 1000,
|
|
260
260
|
});
|
|
@@ -273,7 +273,7 @@ export function OxyServicesAssetsMixin(Base) {
|
|
|
273
273
|
params.variant = variant;
|
|
274
274
|
if (expiresIn)
|
|
275
275
|
params.expiresIn = expiresIn;
|
|
276
|
-
return await this.makeRequest('GET', `/
|
|
276
|
+
return await this.makeRequest('GET', `/assets/${fileId}/url`, params, {
|
|
277
277
|
cache: true,
|
|
278
278
|
cacheTTL: 10 * 60 * 1000,
|
|
279
279
|
});
|
|
@@ -287,7 +287,7 @@ export function OxyServicesAssetsMixin(Base) {
|
|
|
287
287
|
*/
|
|
288
288
|
async assetRestore(fileId) {
|
|
289
289
|
try {
|
|
290
|
-
return await this.makeRequest('POST', `/
|
|
290
|
+
return await this.makeRequest('POST', `/assets/${fileId}/restore`, undefined, { cache: false });
|
|
291
291
|
}
|
|
292
292
|
catch (error) {
|
|
293
293
|
throw this.handleError(error);
|
|
@@ -299,7 +299,7 @@ export function OxyServicesAssetsMixin(Base) {
|
|
|
299
299
|
async assetDelete(fileId, force = false) {
|
|
300
300
|
try {
|
|
301
301
|
const params = force ? { force: 'true' } : undefined;
|
|
302
|
-
return await this.makeRequest('DELETE', `/
|
|
302
|
+
return await this.makeRequest('DELETE', `/assets/${fileId}`, params, { cache: false });
|
|
303
303
|
}
|
|
304
304
|
catch (error) {
|
|
305
305
|
throw this.handleError(error);
|
|
@@ -322,7 +322,7 @@ export function OxyServicesAssetsMixin(Base) {
|
|
|
322
322
|
*/
|
|
323
323
|
async assetUpdateVisibility(fileId, visibility) {
|
|
324
324
|
try {
|
|
325
|
-
return await this.makeRequest('PATCH', `/
|
|
325
|
+
return await this.makeRequest('PATCH', `/assets/${fileId}/visibility`, {
|
|
326
326
|
visibility
|
|
327
327
|
}, { cache: false });
|
|
328
328
|
}
|
|
@@ -360,7 +360,7 @@ export function OxyServicesAssetsMixin(Base) {
|
|
|
360
360
|
params.variant = variant;
|
|
361
361
|
if (expiresIn)
|
|
362
362
|
params.expiresIn = expiresIn;
|
|
363
|
-
const urlRes = await this.makeRequest('GET', `/
|
|
363
|
+
const urlRes = await this.makeRequest('GET', `/assets/${encodeURIComponent(fileId)}/url`, Object.keys(params).length ? params : undefined, {
|
|
364
364
|
cache: true,
|
|
365
365
|
cacheTTL: cacheTTL ?? 10 * 60 * 1000,
|
|
366
366
|
});
|
|
@@ -40,7 +40,7 @@ export function OxyServicesAuthMixin(Base) {
|
|
|
40
40
|
if (this._serviceToken && this._serviceTokenExp > Date.now() + 60000) {
|
|
41
41
|
return this._serviceToken;
|
|
42
42
|
}
|
|
43
|
-
const response = await this.makeRequest('POST', '/
|
|
43
|
+
const response = await this.makeRequest('POST', '/auth/service-token', { apiKey: key, apiSecret: secret }, { cache: false, retry: false });
|
|
44
44
|
this._serviceToken = response.token;
|
|
45
45
|
this._serviceTokenExp = Date.now() + response.expiresIn * 1000;
|
|
46
46
|
return this._serviceToken;
|
|
@@ -74,7 +74,7 @@ export function OxyServicesAuthMixin(Base) {
|
|
|
74
74
|
*/
|
|
75
75
|
async register(publicKey, signature, timestamp) {
|
|
76
76
|
try {
|
|
77
|
-
const res = await this.makeRequest('POST', '/
|
|
77
|
+
const res = await this.makeRequest('POST', '/auth/register', {
|
|
78
78
|
publicKey,
|
|
79
79
|
signature,
|
|
80
80
|
timestamp,
|
|
@@ -96,7 +96,7 @@ export function OxyServicesAuthMixin(Base) {
|
|
|
96
96
|
*/
|
|
97
97
|
async requestChallenge(publicKey) {
|
|
98
98
|
try {
|
|
99
|
-
return await this.makeRequest('POST', '/
|
|
99
|
+
return await this.makeRequest('POST', '/auth/challenge', {
|
|
100
100
|
publicKey,
|
|
101
101
|
}, { cache: false });
|
|
102
102
|
}
|
|
@@ -116,7 +116,7 @@ export function OxyServicesAuthMixin(Base) {
|
|
|
116
116
|
*/
|
|
117
117
|
async verifyChallenge(publicKey, challenge, signature, timestamp, deviceName, deviceFingerprint) {
|
|
118
118
|
try {
|
|
119
|
-
return await this.makeRequest('POST', '/
|
|
119
|
+
return await this.makeRequest('POST', '/auth/verify', {
|
|
120
120
|
publicKey,
|
|
121
121
|
challenge,
|
|
122
122
|
signature,
|
|
@@ -134,7 +134,7 @@ export function OxyServicesAuthMixin(Base) {
|
|
|
134
134
|
*/
|
|
135
135
|
async checkPublicKeyRegistered(publicKey) {
|
|
136
136
|
try {
|
|
137
|
-
return await this.makeRequest('GET', `/
|
|
137
|
+
return await this.makeRequest('GET', `/auth/check-publickey/${encodeURIComponent(publicKey)}`, undefined, { cache: false });
|
|
138
138
|
}
|
|
139
139
|
catch (error) {
|
|
140
140
|
throw this.handleError(error);
|
|
@@ -145,7 +145,7 @@ export function OxyServicesAuthMixin(Base) {
|
|
|
145
145
|
*/
|
|
146
146
|
async getUserByPublicKey(publicKey) {
|
|
147
147
|
try {
|
|
148
|
-
return await this.makeRequest('GET', `/
|
|
148
|
+
return await this.makeRequest('GET', `/auth/user/${encodeURIComponent(publicKey)}`, undefined, { cache: true, cacheTTL: 2 * 60 * 1000 });
|
|
149
149
|
}
|
|
150
150
|
catch (error) {
|
|
151
151
|
throw this.handleError(error);
|
|
@@ -156,7 +156,7 @@ export function OxyServicesAuthMixin(Base) {
|
|
|
156
156
|
*/
|
|
157
157
|
async getUserBySession(sessionId) {
|
|
158
158
|
try {
|
|
159
|
-
return await this.makeRequest('GET', `/
|
|
159
|
+
return await this.makeRequest('GET', `/session/user/${sessionId}`, undefined, {
|
|
160
160
|
cache: true,
|
|
161
161
|
cacheTTL: 2 * 60 * 1000,
|
|
162
162
|
});
|
|
@@ -174,7 +174,7 @@ export function OxyServicesAuthMixin(Base) {
|
|
|
174
174
|
return [];
|
|
175
175
|
}
|
|
176
176
|
const uniqueSessionIds = Array.from(new Set(sessionIds)).sort();
|
|
177
|
-
return await this.makeRequest('POST', '/
|
|
177
|
+
return await this.makeRequest('POST', '/session/users/batch', { sessionIds: uniqueSessionIds }, {
|
|
178
178
|
cache: true,
|
|
179
179
|
cacheTTL: 2 * 60 * 1000,
|
|
180
180
|
deduplicate: true,
|
|
@@ -189,7 +189,7 @@ export function OxyServicesAuthMixin(Base) {
|
|
|
189
189
|
*/
|
|
190
190
|
async getTokenBySession(sessionId) {
|
|
191
191
|
try {
|
|
192
|
-
const res = await this.makeRequest('GET', `/
|
|
192
|
+
const res = await this.makeRequest('GET', `/session/token/${sessionId}`, undefined, { cache: false, retry: false });
|
|
193
193
|
this.setTokens(res.accessToken);
|
|
194
194
|
return res;
|
|
195
195
|
}
|
|
@@ -202,7 +202,7 @@ export function OxyServicesAuthMixin(Base) {
|
|
|
202
202
|
*/
|
|
203
203
|
async getSessionsBySessionId(sessionId) {
|
|
204
204
|
try {
|
|
205
|
-
return await this.makeRequest('GET', `/
|
|
205
|
+
return await this.makeRequest('GET', `/session/sessions/${sessionId}`, undefined, {
|
|
206
206
|
cache: false,
|
|
207
207
|
});
|
|
208
208
|
}
|
|
@@ -216,8 +216,8 @@ export function OxyServicesAuthMixin(Base) {
|
|
|
216
216
|
async logoutSession(sessionId, targetSessionId) {
|
|
217
217
|
try {
|
|
218
218
|
const url = targetSessionId
|
|
219
|
-
? `/
|
|
220
|
-
: `/
|
|
219
|
+
? `/session/logout/${sessionId}/${targetSessionId}`
|
|
220
|
+
: `/session/logout/${sessionId}`;
|
|
221
221
|
await this.makeRequest('POST', url, undefined, { cache: false });
|
|
222
222
|
}
|
|
223
223
|
catch (error) {
|
|
@@ -229,7 +229,7 @@ export function OxyServicesAuthMixin(Base) {
|
|
|
229
229
|
*/
|
|
230
230
|
async logoutAllSessions(sessionId) {
|
|
231
231
|
try {
|
|
232
|
-
await this.makeRequest('POST', `/
|
|
232
|
+
await this.makeRequest('POST', `/session/logout-all/${sessionId}`, undefined, { cache: false });
|
|
233
233
|
}
|
|
234
234
|
catch (error) {
|
|
235
235
|
throw this.handleError(error);
|
|
@@ -245,11 +245,11 @@ export function OxyServicesAuthMixin(Base) {
|
|
|
245
245
|
urlParams.deviceFingerprint = options.deviceFingerprint;
|
|
246
246
|
if (options.useHeaderValidation)
|
|
247
247
|
urlParams.useHeaderValidation = 'true';
|
|
248
|
-
return await this.makeRequest('GET', `/
|
|
248
|
+
return await this.makeRequest('GET', `/session/validate/${sessionId}`, urlParams, { cache: false });
|
|
249
249
|
}
|
|
250
250
|
catch (error) {
|
|
251
251
|
// Session is invalid — clear any cached user data for this session (#196)
|
|
252
|
-
this.clearCacheEntry(`GET:/
|
|
252
|
+
this.clearCacheEntry(`GET:/session/user/${sessionId}`);
|
|
253
253
|
throw this.handleError(error);
|
|
254
254
|
}
|
|
255
255
|
}
|
|
@@ -258,7 +258,7 @@ export function OxyServicesAuthMixin(Base) {
|
|
|
258
258
|
*/
|
|
259
259
|
async checkUsernameAvailability(username) {
|
|
260
260
|
try {
|
|
261
|
-
return await this.makeRequest('GET', `/
|
|
261
|
+
return await this.makeRequest('GET', `/auth/check-username/${username}`, undefined, { cache: false });
|
|
262
262
|
}
|
|
263
263
|
catch (error) {
|
|
264
264
|
throw this.handleError(error);
|
|
@@ -269,7 +269,7 @@ export function OxyServicesAuthMixin(Base) {
|
|
|
269
269
|
*/
|
|
270
270
|
async checkEmailAvailability(email) {
|
|
271
271
|
try {
|
|
272
|
-
return await this.makeRequest('GET', `/
|
|
272
|
+
return await this.makeRequest('GET', `/auth/check-email/${email}`, undefined, { cache: false });
|
|
273
273
|
}
|
|
274
274
|
catch (error) {
|
|
275
275
|
throw this.handleError(error);
|
|
@@ -280,7 +280,7 @@ export function OxyServicesAuthMixin(Base) {
|
|
|
280
280
|
*/
|
|
281
281
|
async signUp(username, email, password, deviceName, deviceFingerprint) {
|
|
282
282
|
try {
|
|
283
|
-
return await this.makeRequest('POST', '/
|
|
283
|
+
return await this.makeRequest('POST', '/auth/signup', {
|
|
284
284
|
username,
|
|
285
285
|
email,
|
|
286
286
|
password,
|
|
@@ -297,7 +297,7 @@ export function OxyServicesAuthMixin(Base) {
|
|
|
297
297
|
*/
|
|
298
298
|
async signIn(identifier, password, deviceName, deviceFingerprint) {
|
|
299
299
|
try {
|
|
300
|
-
return await this.makeRequest('POST', '/
|
|
300
|
+
return await this.makeRequest('POST', '/auth/login', {
|
|
301
301
|
identifier,
|
|
302
302
|
password,
|
|
303
303
|
deviceName,
|