@oxyhq/services 5.13.1 → 5.13.3
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 +71 -0
- package/lib/commonjs/core/HttpClient.js +238 -0
- package/lib/commonjs/core/HttpClient.js.map +1 -0
- package/lib/commonjs/core/OxyServices.js +538 -332
- package/lib/commonjs/core/OxyServices.js.map +1 -1
- package/lib/commonjs/core/RequestManager.js +199 -0
- package/lib/commonjs/core/RequestManager.js.map +1 -0
- package/lib/commonjs/core/index.js +38 -1
- package/lib/commonjs/core/index.js.map +1 -1
- package/lib/commonjs/index.js +36 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/components/Avatar.js +94 -27
- package/lib/commonjs/ui/components/Avatar.js.map +1 -1
- package/lib/commonjs/ui/components/FollowButton.js +1 -0
- package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
- package/lib/commonjs/ui/components/internal/TextField.js +13 -8
- package/lib/commonjs/ui/components/internal/TextField.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +183 -224
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionSocket.js +80 -22
- package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/commonjs/ui/index.js +4 -1
- package/lib/commonjs/ui/index.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js +32 -2
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +101 -59
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/FileManagementScreen.js +3 -2
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/LanguageSelectorScreen.js +75 -117
- package/lib/commonjs/ui/screens/LanguageSelectorScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SignInScreen.js +0 -11
- package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SignUpScreen.js +14 -16
- package/lib/commonjs/ui/screens/SignUpScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js +50 -18
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js +10 -10
- package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js.map +1 -1
- package/lib/commonjs/ui/screens/steps/SignInPasswordStep.js +16 -26
- package/lib/commonjs/ui/screens/steps/SignInPasswordStep.js.map +1 -1
- package/lib/commonjs/ui/screens/steps/SignInUsernameStep.js +104 -212
- package/lib/commonjs/ui/screens/steps/SignInUsernameStep.js.map +1 -1
- package/lib/commonjs/ui/stores/accountStore.js +237 -0
- package/lib/commonjs/ui/stores/accountStore.js.map +1 -0
- package/lib/commonjs/ui/stores/authStore.js +2 -1
- package/lib/commonjs/ui/stores/authStore.js.map +1 -1
- package/lib/commonjs/ui/styles/authStyles.js +14 -7
- package/lib/commonjs/ui/styles/authStyles.js.map +1 -1
- package/lib/commonjs/utils/asyncUtils.js +9 -22
- package/lib/commonjs/utils/asyncUtils.js.map +1 -1
- package/lib/commonjs/utils/cache.js +259 -0
- package/lib/commonjs/utils/cache.js.map +1 -0
- package/lib/commonjs/utils/index.js +99 -0
- package/lib/commonjs/utils/index.js.map +1 -1
- package/lib/commonjs/utils/languageUtils.js +159 -0
- package/lib/commonjs/utils/languageUtils.js.map +1 -0
- package/lib/commonjs/utils/requestUtils.js +217 -0
- package/lib/commonjs/utils/requestUtils.js.map +1 -0
- package/lib/commonjs/utils/sessionUtils.js +191 -0
- package/lib/commonjs/utils/sessionUtils.js.map +1 -0
- package/lib/module/core/HttpClient.js +232 -0
- package/lib/module/core/HttpClient.js.map +1 -0
- package/lib/module/core/OxyServices.js +536 -326
- package/lib/module/core/OxyServices.js.map +1 -1
- package/lib/module/core/RequestManager.js +194 -0
- package/lib/module/core/RequestManager.js.map +1 -0
- package/lib/module/core/index.js +2 -0
- package/lib/module/core/index.js.map +1 -1
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/components/Avatar.js +94 -27
- package/lib/module/ui/components/Avatar.js.map +1 -1
- package/lib/module/ui/components/FollowButton.js +1 -0
- package/lib/module/ui/components/FollowButton.js.map +1 -1
- package/lib/module/ui/components/internal/TextField.js +13 -8
- package/lib/module/ui/components/internal/TextField.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +182 -223
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/hooks/useSessionSocket.js +80 -22
- package/lib/module/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/module/ui/index.js +4 -2
- package/lib/module/ui/index.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js +33 -2
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSwitcherScreen.js +102 -60
- package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/module/ui/screens/FileManagementScreen.js +3 -2
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/LanguageSelectorScreen.js +73 -117
- package/lib/module/ui/screens/LanguageSelectorScreen.js.map +1 -1
- package/lib/module/ui/screens/SignInScreen.js +0 -11
- package/lib/module/ui/screens/SignInScreen.js.map +1 -1
- package/lib/module/ui/screens/SignUpScreen.js +14 -16
- package/lib/module/ui/screens/SignUpScreen.js.map +1 -1
- package/lib/module/ui/screens/WelcomeNewUserScreen.js +50 -18
- package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/module/ui/screens/internal/SignInPasswordStep.js +10 -10
- package/lib/module/ui/screens/internal/SignInPasswordStep.js.map +1 -1
- package/lib/module/ui/screens/steps/SignInPasswordStep.js +16 -26
- package/lib/module/ui/screens/steps/SignInPasswordStep.js.map +1 -1
- package/lib/module/ui/screens/steps/SignInUsernameStep.js +105 -214
- package/lib/module/ui/screens/steps/SignInUsernameStep.js.map +1 -1
- package/lib/module/ui/stores/accountStore.js +229 -0
- package/lib/module/ui/stores/accountStore.js.map +1 -0
- package/lib/module/ui/stores/authStore.js +2 -1
- package/lib/module/ui/stores/authStore.js.map +1 -1
- package/lib/module/ui/styles/authStyles.js +14 -7
- package/lib/module/ui/styles/authStyles.js.map +1 -1
- package/lib/module/utils/asyncUtils.js +10 -22
- package/lib/module/utils/asyncUtils.js.map +1 -1
- package/lib/module/utils/cache.js +250 -0
- package/lib/module/utils/cache.js.map +1 -0
- package/lib/module/utils/index.js +7 -0
- package/lib/module/utils/index.js.map +1 -1
- package/lib/module/utils/languageUtils.js +151 -0
- package/lib/module/utils/languageUtils.js.map +1 -0
- package/lib/module/utils/requestUtils.js +210 -0
- package/lib/module/utils/requestUtils.js.map +1 -0
- package/lib/module/utils/sessionUtils.js +180 -0
- package/lib/module/utils/sessionUtils.js.map +1 -0
- package/lib/typescript/core/HttpClient.d.ts +64 -0
- package/lib/typescript/core/HttpClient.d.ts.map +1 -0
- package/lib/typescript/core/OxyServices.d.ts +88 -71
- package/lib/typescript/core/OxyServices.d.ts.map +1 -1
- package/lib/typescript/core/RequestManager.d.ts +67 -0
- package/lib/typescript/core/RequestManager.d.ts.map +1 -0
- package/lib/typescript/core/index.d.ts +2 -0
- package/lib/typescript/core/index.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +2 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/models/interfaces.d.ts +15 -0
- package/lib/typescript/models/interfaces.d.ts.map +1 -1
- package/lib/typescript/models/session.d.ts +1 -0
- package/lib/typescript/models/session.d.ts.map +1 -1
- package/lib/typescript/ui/components/Avatar.d.ts +6 -7
- package/lib/typescript/ui/components/Avatar.d.ts.map +1 -1
- package/lib/typescript/ui/components/FollowButton.d.ts.map +1 -1
- package/lib/typescript/ui/components/internal/TextField.d.ts.map +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts +4 -0
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useSessionSocket.d.ts.map +1 -1
- package/lib/typescript/ui/index.d.ts +2 -2
- package/lib/typescript/ui/index.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountSwitcherScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/LanguageSelectorScreen.d.ts +3 -3
- package/lib/typescript/ui/screens/LanguageSelectorScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/SignInScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/SignUpScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts.map +1 -1
- package/lib/typescript/ui/screens/steps/SignInPasswordStep.d.ts.map +1 -1
- package/lib/typescript/ui/screens/steps/SignInUsernameStep.d.ts.map +1 -1
- package/lib/typescript/ui/stores/accountStore.d.ts +34 -0
- package/lib/typescript/ui/stores/accountStore.d.ts.map +1 -0
- package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
- package/lib/typescript/ui/styles/authStyles.d.ts +18 -2
- package/lib/typescript/ui/styles/authStyles.d.ts.map +1 -1
- package/lib/typescript/utils/asyncUtils.d.ts +2 -0
- package/lib/typescript/utils/asyncUtils.d.ts.map +1 -1
- package/lib/typescript/utils/cache.d.ts +128 -0
- package/lib/typescript/utils/cache.d.ts.map +1 -0
- package/lib/typescript/utils/index.d.ts +4 -0
- package/lib/typescript/utils/index.d.ts.map +1 -1
- package/lib/typescript/utils/languageUtils.d.ts +38 -0
- package/lib/typescript/utils/languageUtils.d.ts.map +1 -0
- package/lib/typescript/utils/requestUtils.d.ts +122 -0
- package/lib/typescript/utils/requestUtils.d.ts.map +1 -0
- package/lib/typescript/utils/sessionUtils.d.ts +55 -0
- package/lib/typescript/utils/sessionUtils.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/core/HttpClient.ts +277 -0
- package/src/core/OxyServices.ts +466 -351
- package/src/core/RequestManager.ts +240 -0
- package/src/core/index.ts +10 -0
- package/src/index.ts +10 -0
- package/src/models/interfaces.ts +19 -0
- package/src/models/session.ts +1 -1
- package/src/ui/components/Avatar.tsx +151 -35
- package/src/ui/components/FollowButton.tsx +1 -0
- package/src/ui/components/internal/TextField.tsx +7 -6
- package/src/ui/context/OxyContext.tsx +213 -217
- package/src/ui/hooks/useSessionSocket.ts +72 -18
- package/src/ui/index.ts +4 -1
- package/src/ui/screens/AccountSettingsScreen.tsx +34 -2
- package/src/ui/screens/AccountSwitcherScreen.tsx +102 -68
- package/src/ui/screens/FileManagementScreen.tsx +1 -1
- package/src/ui/screens/LanguageSelectorScreen.tsx +86 -143
- package/src/ui/screens/SignInScreen.tsx +0 -7
- package/src/ui/screens/SignUpScreen.tsx +14 -15
- package/src/ui/screens/WelcomeNewUserScreen.tsx +52 -15
- package/src/ui/screens/internal/SignInPasswordStep.tsx +4 -6
- package/src/ui/screens/steps/SignInPasswordStep.tsx +4 -8
- package/src/ui/screens/steps/SignInUsernameStep.tsx +110 -256
- package/src/ui/stores/accountStore.ts +285 -0
- package/src/ui/stores/authStore.ts +2 -1
- package/src/ui/styles/authStyles.ts +14 -7
- package/src/utils/asyncUtils.ts +10 -24
- package/src/utils/cache.ts +264 -0
- package/src/utils/index.ts +19 -0
- package/src/utils/languageUtils.ts +174 -0
- package/src/utils/requestUtils.ts +234 -0
- package/src/utils/sessionUtils.ts +206 -0
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
*
|
|
59
59
|
* See method JSDoc for more details and options.
|
|
60
60
|
*/
|
|
61
|
-
import axios from 'axios';
|
|
62
61
|
import { jwtDecode } from 'jwt-decode';
|
|
62
|
+
import { normalizeLanguageCode, getLanguageMetadata, getLanguageName, getNativeLanguageName } from '../utils/languageUtils';
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
65
|
* OxyConfig - Configuration for OxyServices
|
|
@@ -69,6 +69,8 @@ import { jwtDecode } from 'jwt-decode';
|
|
|
69
69
|
|
|
70
70
|
import { handleHttpError } from '../utils/errorUtils';
|
|
71
71
|
import { buildSearchParams, buildPaginationParams } from '../utils/apiUtils';
|
|
72
|
+
import { HttpClient } from './HttpClient';
|
|
73
|
+
import { RequestManager } from './RequestManager';
|
|
72
74
|
/**
|
|
73
75
|
* Custom error types for better error handling
|
|
74
76
|
*/
|
|
@@ -91,42 +93,12 @@ export class OxyAuthenticationTimeoutError extends OxyAuthenticationError {
|
|
|
91
93
|
* OxyServices - Unified client library for interacting with the Oxy API
|
|
92
94
|
*
|
|
93
95
|
* This class provides all API functionality in one simple, easy-to-use interface.
|
|
94
|
-
*
|
|
96
|
+
* Architecture:
|
|
97
|
+
* - HttpClient: Handles HTTP communication and authentication
|
|
98
|
+
* - RequestManager: Handles caching, deduplication, queuing, and retry
|
|
99
|
+
* - OxyServices: Provides high-level API methods
|
|
95
100
|
*/
|
|
96
|
-
// Centralized token store
|
|
97
|
-
class TokenStore {
|
|
98
|
-
accessToken = null;
|
|
99
|
-
refreshToken = null;
|
|
100
|
-
constructor() {}
|
|
101
|
-
static getInstance() {
|
|
102
|
-
if (!TokenStore.instance) {
|
|
103
|
-
TokenStore.instance = new TokenStore();
|
|
104
|
-
}
|
|
105
|
-
return TokenStore.instance;
|
|
106
|
-
}
|
|
107
|
-
setTokens(accessToken, refreshToken = '') {
|
|
108
|
-
this.accessToken = accessToken;
|
|
109
|
-
this.refreshToken = refreshToken;
|
|
110
|
-
}
|
|
111
|
-
getAccessToken() {
|
|
112
|
-
return this.accessToken;
|
|
113
|
-
}
|
|
114
|
-
getRefreshToken() {
|
|
115
|
-
return this.refreshToken;
|
|
116
|
-
}
|
|
117
|
-
clearTokens() {
|
|
118
|
-
this.accessToken = null;
|
|
119
|
-
this.refreshToken = null;
|
|
120
|
-
}
|
|
121
|
-
hasAccessToken() {
|
|
122
|
-
return !!this.accessToken;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
101
|
export class OxyServices {
|
|
126
|
-
/**
|
|
127
|
-
* Creates a new instance of the OxyServices client
|
|
128
|
-
* @param config - Configuration for the client
|
|
129
|
-
*/
|
|
130
102
|
/**
|
|
131
103
|
* Creates a new instance of the OxyServices client
|
|
132
104
|
* @param config - Configuration for the client
|
|
@@ -134,89 +106,27 @@ export class OxyServices {
|
|
|
134
106
|
* config.cloudURL: Oxy Cloud URL (e.g., https://cloud.oxy.so)
|
|
135
107
|
*/
|
|
136
108
|
constructor(config) {
|
|
137
|
-
this.
|
|
138
|
-
baseURL: config.baseURL,
|
|
139
|
-
timeout: 5000 // 5 second timeout
|
|
140
|
-
});
|
|
109
|
+
this.config = config;
|
|
141
110
|
this.cloudURL = config.cloudURL || OXY_CLOUD_URL;
|
|
142
|
-
|
|
143
|
-
|
|
111
|
+
|
|
112
|
+
// Initialize HTTP client (handles authentication and interceptors)
|
|
113
|
+
this.httpClient = new HttpClient(config);
|
|
114
|
+
|
|
115
|
+
// Initialize request manager (handles caching, deduplication, queuing, retry)
|
|
116
|
+
this.requestManager = new RequestManager(this.httpClient, config);
|
|
144
117
|
}
|
|
145
118
|
|
|
146
119
|
// Test-only utility to reset global tokens between jest tests
|
|
147
120
|
static __resetTokensForTests() {
|
|
148
|
-
|
|
149
|
-
TokenStore.getInstance().clearTokens();
|
|
150
|
-
} catch {}
|
|
121
|
+
HttpClient.__resetTokensForTests();
|
|
151
122
|
}
|
|
152
123
|
|
|
153
124
|
/**
|
|
154
|
-
*
|
|
125
|
+
* Make a request with all performance optimizations
|
|
126
|
+
* This is the main method for all API calls - ensures authentication and performance features
|
|
155
127
|
*/
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
this.client.interceptors.request.use(async req => {
|
|
159
|
-
console.log('🔍 Interceptor - URL:', req.url);
|
|
160
|
-
console.log('🔍 Interceptor - Has token:', this.tokenStore.hasAccessToken());
|
|
161
|
-
const accessToken = this.tokenStore.getAccessToken();
|
|
162
|
-
if (!accessToken) {
|
|
163
|
-
console.log('❌ Interceptor - No token available');
|
|
164
|
-
return req;
|
|
165
|
-
}
|
|
166
|
-
try {
|
|
167
|
-
console.log('✅ Interceptor - Adding Authorization header');
|
|
168
|
-
const decoded = jwtDecode(accessToken);
|
|
169
|
-
const currentTime = Math.floor(Date.now() / 1000);
|
|
170
|
-
|
|
171
|
-
// If token expires in less than 60 seconds, refresh it
|
|
172
|
-
if (decoded.exp && decoded.exp - currentTime < 60) {
|
|
173
|
-
// For session-based tokens, get a new token from the session
|
|
174
|
-
if (decoded.sessionId) {
|
|
175
|
-
try {
|
|
176
|
-
// Create a new axios instance to avoid interceptor recursion
|
|
177
|
-
const refreshClient = axios.create({
|
|
178
|
-
baseURL: this.client.defaults.baseURL,
|
|
179
|
-
timeout: this.client.defaults.timeout
|
|
180
|
-
});
|
|
181
|
-
const res = await refreshClient.get(`/api/session/token/${decoded.sessionId}`);
|
|
182
|
-
this.tokenStore.setTokens(res.data.accessToken);
|
|
183
|
-
req.headers.Authorization = `Bearer ${res.data.accessToken}`;
|
|
184
|
-
console.log('✅ Interceptor - Token refreshed and Authorization header set');
|
|
185
|
-
} catch (refreshError) {
|
|
186
|
-
// If refresh fails, use current token anyway
|
|
187
|
-
req.headers.Authorization = `Bearer ${accessToken}`;
|
|
188
|
-
console.log('❌ Interceptor - Token refresh failed, using current token');
|
|
189
|
-
}
|
|
190
|
-
} else {
|
|
191
|
-
// No session ID, use current token
|
|
192
|
-
req.headers.Authorization = `Bearer ${accessToken}`;
|
|
193
|
-
console.log('✅ Interceptor - No session ID, using current token');
|
|
194
|
-
}
|
|
195
|
-
} else {
|
|
196
|
-
// Add authorization header with current token
|
|
197
|
-
req.headers.Authorization = `Bearer ${accessToken}`;
|
|
198
|
-
console.log('✅ Interceptor - Authorization header set with current token');
|
|
199
|
-
}
|
|
200
|
-
} catch (error) {
|
|
201
|
-
console.log('❌ Interceptor - Error processing token:', error);
|
|
202
|
-
// Even if there's an error, still try to use the token
|
|
203
|
-
req.headers.Authorization = `Bearer ${accessToken}`;
|
|
204
|
-
console.log('⚠️ Interceptor - Using token despite error');
|
|
205
|
-
}
|
|
206
|
-
return req;
|
|
207
|
-
}, error => {
|
|
208
|
-
console.log('❌ Interceptor - Request error:', error);
|
|
209
|
-
return Promise.reject(error);
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
// Response interceptor for handling auth errors
|
|
213
|
-
this.client.interceptors.response.use(response => response, error => {
|
|
214
|
-
if (error.response?.status === 401) {
|
|
215
|
-
console.log('❌ Response interceptor - 401 Unauthorized, clearing tokens');
|
|
216
|
-
this.clearTokens();
|
|
217
|
-
}
|
|
218
|
-
return Promise.reject(error);
|
|
219
|
-
});
|
|
128
|
+
async makeRequest(method, url, data, options = {}) {
|
|
129
|
+
return this.requestManager.request(method, url, data, options);
|
|
220
130
|
}
|
|
221
131
|
|
|
222
132
|
// ============================================================================
|
|
@@ -227,7 +137,35 @@ export class OxyServices {
|
|
|
227
137
|
* Get the configured Oxy API base URL
|
|
228
138
|
*/
|
|
229
139
|
getBaseURL() {
|
|
230
|
-
return this.
|
|
140
|
+
return this.httpClient.getBaseURL();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get performance metrics
|
|
145
|
+
*/
|
|
146
|
+
getMetrics() {
|
|
147
|
+
return this.requestManager.getMetrics();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Clear request cache
|
|
152
|
+
*/
|
|
153
|
+
clearCache() {
|
|
154
|
+
this.requestManager.clearCache();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Clear specific cache entry
|
|
159
|
+
*/
|
|
160
|
+
clearCacheEntry(key) {
|
|
161
|
+
this.requestManager.clearCacheEntry(key);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Get cache statistics
|
|
166
|
+
*/
|
|
167
|
+
getCacheStats() {
|
|
168
|
+
return this.requestManager.getCacheStats();
|
|
231
169
|
}
|
|
232
170
|
|
|
233
171
|
/**
|
|
@@ -237,25 +175,33 @@ export class OxyServices {
|
|
|
237
175
|
return this.cloudURL;
|
|
238
176
|
}
|
|
239
177
|
|
|
178
|
+
/**
|
|
179
|
+
* Get the underlying HTTP client instance
|
|
180
|
+
* Useful for advanced use cases that need direct access to the HttpClient
|
|
181
|
+
*/
|
|
182
|
+
getClient() {
|
|
183
|
+
return this.httpClient;
|
|
184
|
+
}
|
|
185
|
+
|
|
240
186
|
/**
|
|
241
187
|
* Set authentication tokens
|
|
242
188
|
*/
|
|
243
189
|
setTokens(accessToken, refreshToken = '') {
|
|
244
|
-
this.
|
|
190
|
+
this.httpClient.setTokens(accessToken, refreshToken);
|
|
245
191
|
}
|
|
246
192
|
|
|
247
193
|
/**
|
|
248
194
|
* Clear stored authentication tokens
|
|
249
195
|
*/
|
|
250
196
|
clearTokens() {
|
|
251
|
-
this.
|
|
197
|
+
this.httpClient.clearTokens();
|
|
252
198
|
}
|
|
253
199
|
|
|
254
200
|
/**
|
|
255
201
|
* Get the current user ID from the access token
|
|
256
202
|
*/
|
|
257
203
|
getCurrentUserId() {
|
|
258
|
-
const accessToken = this.
|
|
204
|
+
const accessToken = this.httpClient.getAccessToken();
|
|
259
205
|
if (!accessToken) {
|
|
260
206
|
return null;
|
|
261
207
|
}
|
|
@@ -271,21 +217,21 @@ export class OxyServices {
|
|
|
271
217
|
* Check if the client has a valid access token
|
|
272
218
|
*/
|
|
273
219
|
hasAccessToken() {
|
|
274
|
-
return this.
|
|
220
|
+
return this.httpClient.hasAccessToken();
|
|
275
221
|
}
|
|
276
222
|
|
|
277
223
|
/**
|
|
278
224
|
* Check if the client has a valid access token (public method)
|
|
279
225
|
*/
|
|
280
226
|
hasValidToken() {
|
|
281
|
-
return this.
|
|
227
|
+
return this.httpClient.hasAccessToken();
|
|
282
228
|
}
|
|
283
229
|
|
|
284
230
|
/**
|
|
285
231
|
* Get the raw access token (for constructing anchor URLs when needed)
|
|
286
232
|
*/
|
|
287
233
|
getAccessToken() {
|
|
288
|
-
return this.
|
|
234
|
+
return this.httpClient.getAccessToken();
|
|
289
235
|
}
|
|
290
236
|
|
|
291
237
|
/**
|
|
@@ -304,7 +250,7 @@ export class OxyServices {
|
|
|
304
250
|
const checkInterval = 100; // Check every 100ms
|
|
305
251
|
|
|
306
252
|
while (Date.now() - startTime < timeoutMs) {
|
|
307
|
-
if (this.
|
|
253
|
+
if (this.httpClient.hasAccessToken()) {
|
|
308
254
|
return true;
|
|
309
255
|
}
|
|
310
256
|
await new Promise(resolve => setTimeout(resolve, checkInterval));
|
|
@@ -325,15 +271,13 @@ export class OxyServices {
|
|
|
325
271
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
326
272
|
try {
|
|
327
273
|
// First attempt: check if we have a token
|
|
328
|
-
if (!this.
|
|
274
|
+
if (!this.httpClient.hasAccessToken()) {
|
|
329
275
|
if (attempt === 0) {
|
|
330
276
|
// On first attempt, wait briefly for authentication to complete
|
|
331
|
-
console.log(`🔄 ${operationName} - Waiting for authentication...`);
|
|
332
277
|
const authReady = await this.waitForAuthentication(authTimeoutMs);
|
|
333
278
|
if (!authReady) {
|
|
334
279
|
throw new OxyAuthenticationTimeoutError(operationName, authTimeoutMs);
|
|
335
280
|
}
|
|
336
|
-
console.log(`✅ ${operationName} - Authentication ready, proceeding...`);
|
|
337
281
|
} else {
|
|
338
282
|
// On retry attempts, fail immediately if no token
|
|
339
283
|
throw new OxyAuthenticationError(`Authentication required: ${operationName} requires a valid access token.`, 'AUTH_REQUIRED');
|
|
@@ -346,7 +290,6 @@ export class OxyServices {
|
|
|
346
290
|
const isLastAttempt = attempt === maxRetries;
|
|
347
291
|
const isAuthError = error?.response?.status === 401 || error?.code === 'MISSING_TOKEN' || error?.message?.includes('Authentication') || error instanceof OxyAuthenticationError;
|
|
348
292
|
if (isAuthError && !isLastAttempt && !(error instanceof OxyAuthenticationTimeoutError)) {
|
|
349
|
-
console.log(`🔄 ${operationName} - Auth error on attempt ${attempt + 1}, retrying in ${retryDelay}ms...`);
|
|
350
293
|
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
|
351
294
|
continue;
|
|
352
295
|
}
|
|
@@ -371,20 +314,16 @@ export class OxyServices {
|
|
|
371
314
|
return false;
|
|
372
315
|
}
|
|
373
316
|
try {
|
|
374
|
-
const res = await this.
|
|
375
|
-
|
|
317
|
+
const res = await this.makeRequest('GET', '/api/auth/validate', undefined, {
|
|
318
|
+
cache: false,
|
|
319
|
+
retry: false
|
|
320
|
+
});
|
|
321
|
+
return res.valid === true;
|
|
376
322
|
} catch (error) {
|
|
377
323
|
return false;
|
|
378
324
|
}
|
|
379
325
|
}
|
|
380
326
|
|
|
381
|
-
/**
|
|
382
|
-
* Get the HTTP client instance (public for external use)
|
|
383
|
-
*/
|
|
384
|
-
getClient() {
|
|
385
|
-
return this.client;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
327
|
/**
|
|
389
328
|
* Centralized error handling
|
|
390
329
|
*/
|
|
@@ -402,8 +341,9 @@ export class OxyServices {
|
|
|
402
341
|
*/
|
|
403
342
|
async healthCheck() {
|
|
404
343
|
try {
|
|
405
|
-
|
|
406
|
-
|
|
344
|
+
return await this.makeRequest('GET', '/health', undefined, {
|
|
345
|
+
cache: false
|
|
346
|
+
});
|
|
407
347
|
} catch (error) {
|
|
408
348
|
throw this.handleError(error);
|
|
409
349
|
}
|
|
@@ -418,15 +358,17 @@ export class OxyServices {
|
|
|
418
358
|
*/
|
|
419
359
|
async signUp(username, email, password) {
|
|
420
360
|
try {
|
|
421
|
-
const res = await this.
|
|
361
|
+
const res = await this.makeRequest('POST', '/api/auth/signup', {
|
|
422
362
|
username,
|
|
423
363
|
email,
|
|
424
364
|
password
|
|
365
|
+
}, {
|
|
366
|
+
cache: false
|
|
425
367
|
});
|
|
426
|
-
if (!res ||
|
|
368
|
+
if (!res || typeof res === 'object' && Object.keys(res).length === 0) {
|
|
427
369
|
throw new OxyAuthenticationError('Sign up failed', 'SIGNUP_FAILED', 400);
|
|
428
370
|
}
|
|
429
|
-
return res
|
|
371
|
+
return res;
|
|
430
372
|
} catch (error) {
|
|
431
373
|
throw this.handleError(error);
|
|
432
374
|
}
|
|
@@ -437,10 +379,11 @@ export class OxyServices {
|
|
|
437
379
|
*/
|
|
438
380
|
async requestRecovery(identifier) {
|
|
439
381
|
try {
|
|
440
|
-
|
|
382
|
+
return await this.makeRequest('POST', '/api/auth/recover/request', {
|
|
441
383
|
identifier
|
|
384
|
+
}, {
|
|
385
|
+
cache: false
|
|
442
386
|
});
|
|
443
|
-
return res.data;
|
|
444
387
|
} catch (error) {
|
|
445
388
|
throw this.handleError(error);
|
|
446
389
|
}
|
|
@@ -451,11 +394,12 @@ export class OxyServices {
|
|
|
451
394
|
*/
|
|
452
395
|
async verifyRecoveryCode(identifier, code) {
|
|
453
396
|
try {
|
|
454
|
-
|
|
397
|
+
return await this.makeRequest('POST', '/api/auth/recover/verify', {
|
|
455
398
|
identifier,
|
|
456
399
|
code
|
|
400
|
+
}, {
|
|
401
|
+
cache: false
|
|
457
402
|
});
|
|
458
|
-
return res.data;
|
|
459
403
|
} catch (error) {
|
|
460
404
|
throw this.handleError(error);
|
|
461
405
|
}
|
|
@@ -466,12 +410,13 @@ export class OxyServices {
|
|
|
466
410
|
*/
|
|
467
411
|
async resetPassword(identifier, code, newPassword) {
|
|
468
412
|
try {
|
|
469
|
-
|
|
413
|
+
return await this.makeRequest('POST', '/api/auth/recover/reset', {
|
|
470
414
|
identifier,
|
|
471
415
|
code,
|
|
472
416
|
newPassword
|
|
417
|
+
}, {
|
|
418
|
+
cache: false
|
|
473
419
|
});
|
|
474
|
-
return res.data;
|
|
475
420
|
} catch (error) {
|
|
476
421
|
throw this.handleError(error);
|
|
477
422
|
}
|
|
@@ -482,36 +427,39 @@ export class OxyServices {
|
|
|
482
427
|
*/
|
|
483
428
|
async resetPasswordWithTotp(identifier, code, newPassword) {
|
|
484
429
|
try {
|
|
485
|
-
|
|
430
|
+
return await this.makeRequest('POST', '/api/auth/recover/totp/reset', {
|
|
486
431
|
identifier,
|
|
487
432
|
code,
|
|
488
433
|
newPassword
|
|
434
|
+
}, {
|
|
435
|
+
cache: false
|
|
489
436
|
});
|
|
490
|
-
return res.data;
|
|
491
437
|
} catch (error) {
|
|
492
438
|
throw this.handleError(error);
|
|
493
439
|
}
|
|
494
440
|
}
|
|
495
441
|
async resetPasswordWithBackupCode(identifier, backupCode, newPassword) {
|
|
496
442
|
try {
|
|
497
|
-
|
|
443
|
+
return await this.makeRequest('POST', '/api/auth/recover/backup/reset', {
|
|
498
444
|
identifier,
|
|
499
445
|
backupCode,
|
|
500
446
|
newPassword
|
|
447
|
+
}, {
|
|
448
|
+
cache: false
|
|
501
449
|
});
|
|
502
|
-
return res.data;
|
|
503
450
|
} catch (error) {
|
|
504
451
|
throw this.handleError(error);
|
|
505
452
|
}
|
|
506
453
|
}
|
|
507
454
|
async resetPasswordWithRecoveryKey(identifier, recoveryKey, newPassword) {
|
|
508
455
|
try {
|
|
509
|
-
|
|
456
|
+
return await this.makeRequest('POST', '/api/auth/recover/recovery-key/reset', {
|
|
510
457
|
identifier,
|
|
511
458
|
recoveryKey,
|
|
512
459
|
newPassword
|
|
460
|
+
}, {
|
|
461
|
+
cache: false
|
|
513
462
|
});
|
|
514
|
-
return res.data;
|
|
515
463
|
} catch (error) {
|
|
516
464
|
throw this.handleError(error);
|
|
517
465
|
}
|
|
@@ -522,13 +470,14 @@ export class OxyServices {
|
|
|
522
470
|
*/
|
|
523
471
|
async signIn(username, password, deviceName, deviceFingerprint) {
|
|
524
472
|
try {
|
|
525
|
-
|
|
473
|
+
return await this.makeRequest('POST', '/api/auth/login', {
|
|
526
474
|
username,
|
|
527
475
|
password,
|
|
528
476
|
deviceName,
|
|
529
477
|
deviceFingerprint
|
|
478
|
+
}, {
|
|
479
|
+
cache: false
|
|
530
480
|
});
|
|
531
|
-
return res.data;
|
|
532
481
|
} catch (error) {
|
|
533
482
|
throw this.handleError(error);
|
|
534
483
|
}
|
|
@@ -539,11 +488,12 @@ export class OxyServices {
|
|
|
539
488
|
*/
|
|
540
489
|
async verifyTotpLogin(mfaToken, code) {
|
|
541
490
|
try {
|
|
542
|
-
|
|
491
|
+
return await this.makeRequest('POST', '/api/auth/totp/verify-login', {
|
|
543
492
|
mfaToken,
|
|
544
493
|
code
|
|
494
|
+
}, {
|
|
495
|
+
cache: false
|
|
545
496
|
});
|
|
546
|
-
return res.data;
|
|
547
497
|
} catch (error) {
|
|
548
498
|
throw this.handleError(error);
|
|
549
499
|
}
|
|
@@ -554,8 +504,35 @@ export class OxyServices {
|
|
|
554
504
|
*/
|
|
555
505
|
async getUserBySession(sessionId) {
|
|
556
506
|
try {
|
|
557
|
-
|
|
558
|
-
|
|
507
|
+
return await this.makeRequest('GET', `/api/session/user/${sessionId}`, undefined, {
|
|
508
|
+
cache: true,
|
|
509
|
+
cacheTTL: 2 * 60 * 1000 // 2 minutes cache for user data
|
|
510
|
+
});
|
|
511
|
+
} catch (error) {
|
|
512
|
+
throw this.handleError(error);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Batch get multiple user profiles by session IDs (optimized for account switching)
|
|
518
|
+
* Returns array of { sessionId, user } objects
|
|
519
|
+
*/
|
|
520
|
+
async getUsersBySessions(sessionIds) {
|
|
521
|
+
try {
|
|
522
|
+
if (!Array.isArray(sessionIds) || sessionIds.length === 0) {
|
|
523
|
+
return [];
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Deduplicate and sort sessionIds for consistent cache keys
|
|
527
|
+
const uniqueSessionIds = Array.from(new Set(sessionIds)).sort();
|
|
528
|
+
return await this.makeRequest('POST', '/api/session/users/batch', {
|
|
529
|
+
sessionIds: uniqueSessionIds
|
|
530
|
+
}, {
|
|
531
|
+
cache: true,
|
|
532
|
+
cacheTTL: 2 * 60 * 1000,
|
|
533
|
+
// 2 minutes cache
|
|
534
|
+
deduplicate: true // Important for batch requests
|
|
535
|
+
});
|
|
559
536
|
} catch (error) {
|
|
560
537
|
throw this.handleError(error);
|
|
561
538
|
}
|
|
@@ -566,19 +543,15 @@ export class OxyServices {
|
|
|
566
543
|
*/
|
|
567
544
|
async getTokenBySession(sessionId) {
|
|
568
545
|
try {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
} = res.data;
|
|
574
|
-
console.log('🔑 getTokenBySession - Token received:', !!accessToken);
|
|
546
|
+
const res = await this.makeRequest('GET', `/api/session/token/${sessionId}`, undefined, {
|
|
547
|
+
cache: false,
|
|
548
|
+
retry: false
|
|
549
|
+
});
|
|
575
550
|
|
|
576
551
|
// Set the token in the centralized token store
|
|
577
|
-
this.setTokens(accessToken);
|
|
578
|
-
|
|
579
|
-
return res.data;
|
|
552
|
+
this.setTokens(res.accessToken);
|
|
553
|
+
return res;
|
|
580
554
|
} catch (error) {
|
|
581
|
-
console.log('❌ getTokenBySession - Error:', error);
|
|
582
555
|
throw this.handleError(error);
|
|
583
556
|
}
|
|
584
557
|
}
|
|
@@ -588,8 +561,9 @@ export class OxyServices {
|
|
|
588
561
|
*/
|
|
589
562
|
async getSessionsBySessionId(sessionId) {
|
|
590
563
|
try {
|
|
591
|
-
|
|
592
|
-
|
|
564
|
+
return await this.makeRequest('GET', `/api/session/sessions/${sessionId}`, undefined, {
|
|
565
|
+
cache: false
|
|
566
|
+
});
|
|
593
567
|
} catch (error) {
|
|
594
568
|
throw this.handleError(error);
|
|
595
569
|
}
|
|
@@ -601,7 +575,9 @@ export class OxyServices {
|
|
|
601
575
|
async logoutSession(sessionId, targetSessionId) {
|
|
602
576
|
try {
|
|
603
577
|
const url = targetSessionId ? `/api/session/logout/${sessionId}/${targetSessionId}` : `/api/session/logout/${sessionId}`;
|
|
604
|
-
await this.
|
|
578
|
+
await this.makeRequest('POST', url, undefined, {
|
|
579
|
+
cache: false
|
|
580
|
+
});
|
|
605
581
|
} catch (error) {
|
|
606
582
|
throw this.handleError(error);
|
|
607
583
|
}
|
|
@@ -612,7 +588,9 @@ export class OxyServices {
|
|
|
612
588
|
*/
|
|
613
589
|
async logoutAllSessions(sessionId) {
|
|
614
590
|
try {
|
|
615
|
-
await this.
|
|
591
|
+
await this.makeRequest('POST', `/api/session/logout-all/${sessionId}`, undefined, {
|
|
592
|
+
cache: false
|
|
593
|
+
});
|
|
616
594
|
} catch (error) {
|
|
617
595
|
throw this.handleError(error);
|
|
618
596
|
}
|
|
@@ -630,9 +608,13 @@ export class OxyServices {
|
|
|
630
608
|
if (options.useHeaderValidation) {
|
|
631
609
|
params.append('useHeaderValidation', 'true');
|
|
632
610
|
}
|
|
633
|
-
const url = `/api/session/validate/${sessionId}
|
|
634
|
-
const
|
|
635
|
-
|
|
611
|
+
const url = `/api/session/validate/${sessionId}`;
|
|
612
|
+
const urlParams = {};
|
|
613
|
+
if (options.deviceFingerprint) urlParams.deviceFingerprint = options.deviceFingerprint;
|
|
614
|
+
if (options.useHeaderValidation) urlParams.useHeaderValidation = 'true';
|
|
615
|
+
return await this.makeRequest('GET', url, urlParams, {
|
|
616
|
+
cache: false
|
|
617
|
+
});
|
|
636
618
|
} catch (error) {
|
|
637
619
|
throw this.handleError(error);
|
|
638
620
|
}
|
|
@@ -643,8 +625,9 @@ export class OxyServices {
|
|
|
643
625
|
*/
|
|
644
626
|
async checkUsernameAvailability(username) {
|
|
645
627
|
try {
|
|
646
|
-
|
|
647
|
-
|
|
628
|
+
return await this.makeRequest('GET', `/api/auth/check-username/${username}`, undefined, {
|
|
629
|
+
cache: false
|
|
630
|
+
});
|
|
648
631
|
} catch (error) {
|
|
649
632
|
throw this.handleError(error);
|
|
650
633
|
}
|
|
@@ -655,8 +638,9 @@ export class OxyServices {
|
|
|
655
638
|
*/
|
|
656
639
|
async checkEmailAvailability(email) {
|
|
657
640
|
try {
|
|
658
|
-
|
|
659
|
-
|
|
641
|
+
return await this.makeRequest('GET', `/api/auth/check-email/${email}`, undefined, {
|
|
642
|
+
cache: false
|
|
643
|
+
});
|
|
660
644
|
} catch (error) {
|
|
661
645
|
throw this.handleError(error);
|
|
662
646
|
}
|
|
@@ -671,8 +655,10 @@ export class OxyServices {
|
|
|
671
655
|
*/
|
|
672
656
|
async getProfileByUsername(username) {
|
|
673
657
|
try {
|
|
674
|
-
|
|
675
|
-
|
|
658
|
+
return await this.makeRequest('GET', `/api/profiles/username/${username}`, undefined, {
|
|
659
|
+
cache: true,
|
|
660
|
+
cacheTTL: 5 * 60 * 1000 // 5 minutes cache for profiles
|
|
661
|
+
});
|
|
676
662
|
} catch (error) {
|
|
677
663
|
throw this.handleError(error);
|
|
678
664
|
}
|
|
@@ -684,44 +670,36 @@ export class OxyServices {
|
|
|
684
670
|
|
|
685
671
|
async startTotpEnrollment(sessionId) {
|
|
686
672
|
try {
|
|
687
|
-
|
|
673
|
+
// Note: x-session-id header is handled by HttpClient interceptors if needed
|
|
674
|
+
return await this.makeRequest('POST', '/api/auth/totp/enroll/start', {
|
|
688
675
|
sessionId
|
|
689
676
|
}, {
|
|
690
|
-
|
|
691
|
-
'x-session-id': sessionId
|
|
692
|
-
}
|
|
677
|
+
cache: false
|
|
693
678
|
});
|
|
694
|
-
return res.data;
|
|
695
679
|
} catch (error) {
|
|
696
680
|
throw this.handleError(error);
|
|
697
681
|
}
|
|
698
682
|
}
|
|
699
683
|
async verifyTotpEnrollment(sessionId, code) {
|
|
700
684
|
try {
|
|
701
|
-
|
|
685
|
+
return await this.makeRequest('POST', '/api/auth/totp/enroll/verify', {
|
|
702
686
|
sessionId,
|
|
703
687
|
code
|
|
704
688
|
}, {
|
|
705
|
-
|
|
706
|
-
'x-session-id': sessionId
|
|
707
|
-
}
|
|
689
|
+
cache: false
|
|
708
690
|
});
|
|
709
|
-
return res.data;
|
|
710
691
|
} catch (error) {
|
|
711
692
|
throw this.handleError(error);
|
|
712
693
|
}
|
|
713
694
|
}
|
|
714
695
|
async disableTotp(sessionId, code) {
|
|
715
696
|
try {
|
|
716
|
-
|
|
697
|
+
return await this.makeRequest('POST', '/api/auth/totp/disable', {
|
|
717
698
|
sessionId,
|
|
718
699
|
code
|
|
719
700
|
}, {
|
|
720
|
-
|
|
721
|
-
'x-session-id': sessionId
|
|
722
|
-
}
|
|
701
|
+
cache: false
|
|
723
702
|
});
|
|
724
|
-
return res.data;
|
|
725
703
|
} catch (error) {
|
|
726
704
|
throw this.handleError(error);
|
|
727
705
|
}
|
|
@@ -737,8 +715,14 @@ export class OxyServices {
|
|
|
737
715
|
...pagination
|
|
738
716
|
};
|
|
739
717
|
const searchParams = buildSearchParams(params);
|
|
740
|
-
const
|
|
741
|
-
|
|
718
|
+
const paramsObj = {};
|
|
719
|
+
searchParams.forEach((value, key) => {
|
|
720
|
+
paramsObj[key] = value;
|
|
721
|
+
});
|
|
722
|
+
return await this.makeRequest('GET', '/api/profiles/search', paramsObj, {
|
|
723
|
+
cache: true,
|
|
724
|
+
cacheTTL: 2 * 60 * 1000 // 2 minutes cache
|
|
725
|
+
});
|
|
742
726
|
} catch (error) {
|
|
743
727
|
throw this.handleError(error);
|
|
744
728
|
}
|
|
@@ -749,8 +733,9 @@ export class OxyServices {
|
|
|
749
733
|
*/
|
|
750
734
|
async getProfileRecommendations() {
|
|
751
735
|
return this.withAuthRetry(async () => {
|
|
752
|
-
|
|
753
|
-
|
|
736
|
+
return await this.makeRequest('GET', '/api/profiles/recommendations', undefined, {
|
|
737
|
+
cache: true
|
|
738
|
+
});
|
|
754
739
|
}, 'getProfileRecommendations');
|
|
755
740
|
}
|
|
756
741
|
|
|
@@ -759,8 +744,10 @@ export class OxyServices {
|
|
|
759
744
|
*/
|
|
760
745
|
async getUserById(userId) {
|
|
761
746
|
try {
|
|
762
|
-
|
|
763
|
-
|
|
747
|
+
return await this.makeRequest('GET', `/api/users/${userId}`, undefined, {
|
|
748
|
+
cache: true,
|
|
749
|
+
cacheTTL: 5 * 60 * 1000 // 5 minutes cache
|
|
750
|
+
});
|
|
764
751
|
} catch (error) {
|
|
765
752
|
throw this.handleError(error);
|
|
766
753
|
}
|
|
@@ -771,8 +758,10 @@ export class OxyServices {
|
|
|
771
758
|
*/
|
|
772
759
|
async getCurrentUser() {
|
|
773
760
|
return this.withAuthRetry(async () => {
|
|
774
|
-
|
|
775
|
-
|
|
761
|
+
return await this.makeRequest('GET', '/api/users/me', undefined, {
|
|
762
|
+
cache: true,
|
|
763
|
+
cacheTTL: 1 * 60 * 1000 // 1 minute cache for current user
|
|
764
|
+
});
|
|
776
765
|
}, 'getCurrentUser');
|
|
777
766
|
}
|
|
778
767
|
|
|
@@ -781,20 +770,134 @@ export class OxyServices {
|
|
|
781
770
|
*/
|
|
782
771
|
async updateProfile(updates) {
|
|
783
772
|
try {
|
|
784
|
-
|
|
785
|
-
|
|
773
|
+
return await this.makeRequest('PUT', '/api/users/me', updates, {
|
|
774
|
+
cache: false
|
|
775
|
+
});
|
|
786
776
|
} catch (error) {
|
|
787
777
|
throw this.handleError(error);
|
|
788
778
|
}
|
|
789
779
|
}
|
|
790
780
|
|
|
781
|
+
// ============================================================================
|
|
782
|
+
// LANGUAGE METHODS
|
|
783
|
+
// ============================================================================
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Get the current language from storage or user profile
|
|
787
|
+
* @param storageKeyPrefix - Optional prefix for storage key (default: 'oxy_session')
|
|
788
|
+
* @returns The current language code (e.g., 'en-US') or null if not set
|
|
789
|
+
*/
|
|
790
|
+
async getCurrentLanguage(storageKeyPrefix = 'oxy_session') {
|
|
791
|
+
try {
|
|
792
|
+
// First try to get from user profile if authenticated
|
|
793
|
+
try {
|
|
794
|
+
const user = await this.getCurrentUser();
|
|
795
|
+
const userLanguage = user?.language;
|
|
796
|
+
if (userLanguage) {
|
|
797
|
+
return normalizeLanguageCode(userLanguage) || userLanguage;
|
|
798
|
+
}
|
|
799
|
+
} catch (e) {
|
|
800
|
+
// User not authenticated or error, continue to storage
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
// Fall back to storage
|
|
804
|
+
const storage = await this.getStorage();
|
|
805
|
+
const storageKey = `${storageKeyPrefix}_language`;
|
|
806
|
+
const storedLanguage = await storage.getItem(storageKey);
|
|
807
|
+
if (storedLanguage) {
|
|
808
|
+
return normalizeLanguageCode(storedLanguage) || storedLanguage;
|
|
809
|
+
}
|
|
810
|
+
return null;
|
|
811
|
+
} catch (error) {
|
|
812
|
+
if (__DEV__) {
|
|
813
|
+
console.warn('Failed to get current language:', error);
|
|
814
|
+
}
|
|
815
|
+
return null;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* Get the current language with metadata (name, nativeName, etc.)
|
|
821
|
+
* @param storageKeyPrefix - Optional prefix for storage key (default: 'oxy_session')
|
|
822
|
+
* @returns Language metadata object or null if not set
|
|
823
|
+
*/
|
|
824
|
+
async getCurrentLanguageMetadata(storageKeyPrefix = 'oxy_session') {
|
|
825
|
+
const languageCode = await this.getCurrentLanguage(storageKeyPrefix);
|
|
826
|
+
return getLanguageMetadata(languageCode);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* Get the current language name (e.g., 'English')
|
|
831
|
+
* @param storageKeyPrefix - Optional prefix for storage key (default: 'oxy_session')
|
|
832
|
+
* @returns Language name or null if not set
|
|
833
|
+
*/
|
|
834
|
+
async getCurrentLanguageName(storageKeyPrefix = 'oxy_session') {
|
|
835
|
+
const languageCode = await this.getCurrentLanguage(storageKeyPrefix);
|
|
836
|
+
if (!languageCode) return null;
|
|
837
|
+
return getLanguageName(languageCode);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* Get the current native language name (e.g., 'Español')
|
|
842
|
+
* @param storageKeyPrefix - Optional prefix for storage key (default: 'oxy_session')
|
|
843
|
+
* @returns Native language name or null if not set
|
|
844
|
+
*/
|
|
845
|
+
async getCurrentNativeLanguageName(storageKeyPrefix = 'oxy_session') {
|
|
846
|
+
const languageCode = await this.getCurrentLanguage(storageKeyPrefix);
|
|
847
|
+
if (!languageCode) return null;
|
|
848
|
+
return getNativeLanguageName(languageCode);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* Get appropriate storage for the platform (similar to DeviceManager)
|
|
853
|
+
* @private
|
|
854
|
+
*/
|
|
855
|
+
async getStorage() {
|
|
856
|
+
const isReactNative = typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
|
|
857
|
+
if (isReactNative) {
|
|
858
|
+
try {
|
|
859
|
+
const asyncStorageModule = await import('@react-native-async-storage/async-storage');
|
|
860
|
+
const storage = asyncStorageModule.default;
|
|
861
|
+
return {
|
|
862
|
+
getItem: storage.getItem.bind(storage),
|
|
863
|
+
setItem: storage.setItem.bind(storage),
|
|
864
|
+
removeItem: storage.removeItem.bind(storage)
|
|
865
|
+
};
|
|
866
|
+
} catch (error) {
|
|
867
|
+
console.error('AsyncStorage not available in React Native:', error);
|
|
868
|
+
throw new Error('AsyncStorage is required in React Native environment');
|
|
869
|
+
}
|
|
870
|
+
} else {
|
|
871
|
+
// Use localStorage for web
|
|
872
|
+
return {
|
|
873
|
+
getItem: async key => {
|
|
874
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
875
|
+
return localStorage.getItem(key);
|
|
876
|
+
}
|
|
877
|
+
return null;
|
|
878
|
+
},
|
|
879
|
+
setItem: async (key, value) => {
|
|
880
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
881
|
+
localStorage.setItem(key, value);
|
|
882
|
+
}
|
|
883
|
+
},
|
|
884
|
+
removeItem: async key => {
|
|
885
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
886
|
+
localStorage.removeItem(key);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
|
|
791
893
|
/**
|
|
792
894
|
* Update user by ID (admin function)
|
|
793
895
|
*/
|
|
794
896
|
async updateUser(userId, updates) {
|
|
795
897
|
try {
|
|
796
|
-
|
|
797
|
-
|
|
898
|
+
return await this.makeRequest('PUT', `/api/users/${userId}`, updates, {
|
|
899
|
+
cache: false
|
|
900
|
+
});
|
|
798
901
|
} catch (error) {
|
|
799
902
|
throw this.handleError(error);
|
|
800
903
|
}
|
|
@@ -805,8 +908,9 @@ export class OxyServices {
|
|
|
805
908
|
*/
|
|
806
909
|
async followUser(userId) {
|
|
807
910
|
try {
|
|
808
|
-
|
|
809
|
-
|
|
911
|
+
return await this.makeRequest('POST', `/api/users/${userId}/follow`, undefined, {
|
|
912
|
+
cache: false
|
|
913
|
+
});
|
|
810
914
|
} catch (error) {
|
|
811
915
|
throw this.handleError(error);
|
|
812
916
|
}
|
|
@@ -817,8 +921,9 @@ export class OxyServices {
|
|
|
817
921
|
*/
|
|
818
922
|
async unfollowUser(userId) {
|
|
819
923
|
try {
|
|
820
|
-
|
|
821
|
-
|
|
924
|
+
return await this.makeRequest('DELETE', `/api/users/${userId}/follow`, undefined, {
|
|
925
|
+
cache: false
|
|
926
|
+
});
|
|
822
927
|
} catch (error) {
|
|
823
928
|
throw this.handleError(error);
|
|
824
929
|
}
|
|
@@ -829,8 +934,10 @@ export class OxyServices {
|
|
|
829
934
|
*/
|
|
830
935
|
async getFollowStatus(userId) {
|
|
831
936
|
try {
|
|
832
|
-
|
|
833
|
-
|
|
937
|
+
return await this.makeRequest('GET', `/api/users/${userId}/follow-status`, undefined, {
|
|
938
|
+
cache: true,
|
|
939
|
+
cacheTTL: 1 * 60 * 1000 // 1 minute cache
|
|
940
|
+
});
|
|
834
941
|
} catch (error) {
|
|
835
942
|
throw this.handleError(error);
|
|
836
943
|
}
|
|
@@ -842,8 +949,15 @@ export class OxyServices {
|
|
|
842
949
|
async getUserFollowers(userId, pagination) {
|
|
843
950
|
try {
|
|
844
951
|
const params = buildPaginationParams(pagination || {});
|
|
845
|
-
const
|
|
846
|
-
|
|
952
|
+
const response = await this.makeRequest('GET', `/api/users/${userId}/followers`, params, {
|
|
953
|
+
cache: true,
|
|
954
|
+
cacheTTL: 2 * 60 * 1000 // 2 minutes cache
|
|
955
|
+
});
|
|
956
|
+
return {
|
|
957
|
+
followers: response.data || [],
|
|
958
|
+
total: response.pagination.total,
|
|
959
|
+
hasMore: response.pagination.hasMore
|
|
960
|
+
};
|
|
847
961
|
} catch (error) {
|
|
848
962
|
throw this.handleError(error);
|
|
849
963
|
}
|
|
@@ -855,8 +969,15 @@ export class OxyServices {
|
|
|
855
969
|
async getUserFollowing(userId, pagination) {
|
|
856
970
|
try {
|
|
857
971
|
const params = buildPaginationParams(pagination || {});
|
|
858
|
-
const
|
|
859
|
-
|
|
972
|
+
const response = await this.makeRequest('GET', `/api/users/${userId}/following`, params, {
|
|
973
|
+
cache: true,
|
|
974
|
+
cacheTTL: 2 * 60 * 1000 // 2 minutes cache
|
|
975
|
+
});
|
|
976
|
+
return {
|
|
977
|
+
following: response.data || [],
|
|
978
|
+
total: response.pagination.total,
|
|
979
|
+
hasMore: response.pagination.hasMore
|
|
980
|
+
};
|
|
860
981
|
} catch (error) {
|
|
861
982
|
throw this.handleError(error);
|
|
862
983
|
}
|
|
@@ -867,8 +988,9 @@ export class OxyServices {
|
|
|
867
988
|
*/
|
|
868
989
|
async getNotifications() {
|
|
869
990
|
return this.withAuthRetry(async () => {
|
|
870
|
-
|
|
871
|
-
|
|
991
|
+
return await this.makeRequest('GET', '/api/notifications', undefined, {
|
|
992
|
+
cache: false // Don't cache notifications - always get fresh data
|
|
993
|
+
});
|
|
872
994
|
}, 'getNotifications');
|
|
873
995
|
}
|
|
874
996
|
|
|
@@ -877,8 +999,10 @@ export class OxyServices {
|
|
|
877
999
|
*/
|
|
878
1000
|
async getUnreadCount() {
|
|
879
1001
|
try {
|
|
880
|
-
const res = await this.
|
|
881
|
-
|
|
1002
|
+
const res = await this.makeRequest('GET', '/api/notifications/unread-count', undefined, {
|
|
1003
|
+
cache: false // Don't cache unread count - always get fresh data
|
|
1004
|
+
});
|
|
1005
|
+
return res.count;
|
|
882
1006
|
} catch (error) {
|
|
883
1007
|
throw this.handleError(error);
|
|
884
1008
|
}
|
|
@@ -889,8 +1013,9 @@ export class OxyServices {
|
|
|
889
1013
|
*/
|
|
890
1014
|
async createNotification(data) {
|
|
891
1015
|
try {
|
|
892
|
-
|
|
893
|
-
|
|
1016
|
+
return await this.makeRequest('POST', '/api/notifications', data, {
|
|
1017
|
+
cache: false
|
|
1018
|
+
});
|
|
894
1019
|
} catch (error) {
|
|
895
1020
|
throw this.handleError(error);
|
|
896
1021
|
}
|
|
@@ -901,7 +1026,9 @@ export class OxyServices {
|
|
|
901
1026
|
*/
|
|
902
1027
|
async markNotificationAsRead(notificationId) {
|
|
903
1028
|
try {
|
|
904
|
-
await this.
|
|
1029
|
+
await this.makeRequest('PUT', `/api/notifications/${notificationId}/read`, undefined, {
|
|
1030
|
+
cache: false
|
|
1031
|
+
});
|
|
905
1032
|
} catch (error) {
|
|
906
1033
|
throw this.handleError(error);
|
|
907
1034
|
}
|
|
@@ -912,7 +1039,9 @@ export class OxyServices {
|
|
|
912
1039
|
*/
|
|
913
1040
|
async markAllNotificationsAsRead() {
|
|
914
1041
|
try {
|
|
915
|
-
await this.
|
|
1042
|
+
await this.makeRequest('PUT', '/api/notifications/read-all', undefined, {
|
|
1043
|
+
cache: false
|
|
1044
|
+
});
|
|
916
1045
|
} catch (error) {
|
|
917
1046
|
throw this.handleError(error);
|
|
918
1047
|
}
|
|
@@ -923,7 +1052,9 @@ export class OxyServices {
|
|
|
923
1052
|
*/
|
|
924
1053
|
async deleteNotification(notificationId) {
|
|
925
1054
|
try {
|
|
926
|
-
await this.
|
|
1055
|
+
await this.makeRequest('DELETE', `/api/notifications/${notificationId}`, undefined, {
|
|
1056
|
+
cache: false
|
|
1057
|
+
});
|
|
927
1058
|
} catch (error) {
|
|
928
1059
|
throw this.handleError(error);
|
|
929
1060
|
}
|
|
@@ -938,8 +1069,9 @@ export class OxyServices {
|
|
|
938
1069
|
*/
|
|
939
1070
|
async createPayment(data) {
|
|
940
1071
|
try {
|
|
941
|
-
|
|
942
|
-
|
|
1072
|
+
return await this.makeRequest('POST', '/api/payments', data, {
|
|
1073
|
+
cache: false
|
|
1074
|
+
});
|
|
943
1075
|
} catch (error) {
|
|
944
1076
|
throw this.handleError(error);
|
|
945
1077
|
}
|
|
@@ -950,8 +1082,10 @@ export class OxyServices {
|
|
|
950
1082
|
*/
|
|
951
1083
|
async getPayment(paymentId) {
|
|
952
1084
|
try {
|
|
953
|
-
|
|
954
|
-
|
|
1085
|
+
return await this.makeRequest('GET', `/api/payments/${paymentId}`, undefined, {
|
|
1086
|
+
cache: true,
|
|
1087
|
+
cacheTTL: 5 * 60 * 1000 // 5 minutes cache
|
|
1088
|
+
});
|
|
955
1089
|
} catch (error) {
|
|
956
1090
|
throw this.handleError(error);
|
|
957
1091
|
}
|
|
@@ -962,8 +1096,9 @@ export class OxyServices {
|
|
|
962
1096
|
*/
|
|
963
1097
|
async getUserPayments() {
|
|
964
1098
|
try {
|
|
965
|
-
|
|
966
|
-
|
|
1099
|
+
return await this.makeRequest('GET', '/api/payments/user', undefined, {
|
|
1100
|
+
cache: false // Don't cache user payments - always get fresh data
|
|
1101
|
+
});
|
|
967
1102
|
} catch (error) {
|
|
968
1103
|
throw this.handleError(error);
|
|
969
1104
|
}
|
|
@@ -978,8 +1113,10 @@ export class OxyServices {
|
|
|
978
1113
|
*/
|
|
979
1114
|
async getUserKarma(userId) {
|
|
980
1115
|
try {
|
|
981
|
-
|
|
982
|
-
|
|
1116
|
+
return await this.makeRequest('GET', `/api/karma/${userId}`, undefined, {
|
|
1117
|
+
cache: true,
|
|
1118
|
+
cacheTTL: 2 * 60 * 1000 // 2 minutes cache
|
|
1119
|
+
});
|
|
983
1120
|
} catch (error) {
|
|
984
1121
|
throw this.handleError(error);
|
|
985
1122
|
}
|
|
@@ -990,11 +1127,12 @@ export class OxyServices {
|
|
|
990
1127
|
*/
|
|
991
1128
|
async giveKarma(userId, amount, reason) {
|
|
992
1129
|
try {
|
|
993
|
-
|
|
1130
|
+
return await this.makeRequest('POST', `/api/karma/${userId}/give`, {
|
|
994
1131
|
amount,
|
|
995
1132
|
reason
|
|
1133
|
+
}, {
|
|
1134
|
+
cache: false
|
|
996
1135
|
});
|
|
997
|
-
return res.data;
|
|
998
1136
|
} catch (error) {
|
|
999
1137
|
throw this.handleError(error);
|
|
1000
1138
|
}
|
|
@@ -1005,8 +1143,10 @@ export class OxyServices {
|
|
|
1005
1143
|
*/
|
|
1006
1144
|
async getUserKarmaTotal(userId) {
|
|
1007
1145
|
try {
|
|
1008
|
-
|
|
1009
|
-
|
|
1146
|
+
return await this.makeRequest('GET', `/api/karma/${userId}/total`, undefined, {
|
|
1147
|
+
cache: true,
|
|
1148
|
+
cacheTTL: 2 * 60 * 1000 // 2 minutes cache
|
|
1149
|
+
});
|
|
1010
1150
|
} catch (error) {
|
|
1011
1151
|
throw this.handleError(error);
|
|
1012
1152
|
}
|
|
@@ -1017,11 +1157,13 @@ export class OxyServices {
|
|
|
1017
1157
|
*/
|
|
1018
1158
|
async getUserKarmaHistory(userId, limit, offset) {
|
|
1019
1159
|
try {
|
|
1020
|
-
const params =
|
|
1021
|
-
if (limit) params.
|
|
1022
|
-
if (offset) params.
|
|
1023
|
-
|
|
1024
|
-
|
|
1160
|
+
const params = {};
|
|
1161
|
+
if (limit) params.limit = limit;
|
|
1162
|
+
if (offset) params.offset = offset;
|
|
1163
|
+
return await this.makeRequest('GET', `/api/karma/${userId}/history`, params, {
|
|
1164
|
+
cache: true,
|
|
1165
|
+
cacheTTL: 2 * 60 * 1000 // 2 minutes cache
|
|
1166
|
+
});
|
|
1025
1167
|
} catch (error) {
|
|
1026
1168
|
throw this.handleError(error);
|
|
1027
1169
|
}
|
|
@@ -1032,8 +1174,10 @@ export class OxyServices {
|
|
|
1032
1174
|
*/
|
|
1033
1175
|
async getKarmaLeaderboard() {
|
|
1034
1176
|
try {
|
|
1035
|
-
|
|
1036
|
-
|
|
1177
|
+
return await this.makeRequest('GET', '/api/karma/leaderboard', undefined, {
|
|
1178
|
+
cache: true,
|
|
1179
|
+
cacheTTL: 5 * 60 * 1000 // 5 minutes cache
|
|
1180
|
+
});
|
|
1037
1181
|
} catch (error) {
|
|
1038
1182
|
throw this.handleError(error);
|
|
1039
1183
|
}
|
|
@@ -1044,8 +1188,10 @@ export class OxyServices {
|
|
|
1044
1188
|
*/
|
|
1045
1189
|
async getKarmaRules() {
|
|
1046
1190
|
try {
|
|
1047
|
-
|
|
1048
|
-
|
|
1191
|
+
return await this.makeRequest('GET', '/api/karma/rules', undefined, {
|
|
1192
|
+
cache: true,
|
|
1193
|
+
cacheTTL: 30 * 60 * 1000 // 30 minutes cache (rules don't change often)
|
|
1194
|
+
});
|
|
1049
1195
|
} catch (error) {
|
|
1050
1196
|
throw this.handleError(error);
|
|
1051
1197
|
}
|
|
@@ -1061,8 +1207,9 @@ export class OxyServices {
|
|
|
1061
1207
|
async deleteFile(fileId) {
|
|
1062
1208
|
try {
|
|
1063
1209
|
// Central Asset Service delete with force=true behavior controlled by caller via assetDelete
|
|
1064
|
-
|
|
1065
|
-
|
|
1210
|
+
return await this.makeRequest('DELETE', `/api/assets/${encodeURIComponent(fileId)}`, undefined, {
|
|
1211
|
+
cache: false
|
|
1212
|
+
});
|
|
1066
1213
|
} catch (error) {
|
|
1067
1214
|
throw this.handleError(error);
|
|
1068
1215
|
}
|
|
@@ -1077,7 +1224,7 @@ export class OxyServices {
|
|
|
1077
1224
|
if (variant) params.set('variant', variant);
|
|
1078
1225
|
if (expiresIn) params.set('expiresIn', String(expiresIn));
|
|
1079
1226
|
params.set('fallback', 'placeholderVisible');
|
|
1080
|
-
const token = this.
|
|
1227
|
+
const token = this.httpClient.getAccessToken();
|
|
1081
1228
|
if (token) params.set('token', token);
|
|
1082
1229
|
|
|
1083
1230
|
// Use params.toString() to detect whether there are query params.
|
|
@@ -1103,12 +1250,12 @@ export class OxyServices {
|
|
|
1103
1250
|
*/
|
|
1104
1251
|
async listUserFiles(limit, offset) {
|
|
1105
1252
|
try {
|
|
1106
|
-
const
|
|
1107
|
-
if (limit)
|
|
1108
|
-
if (offset)
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1253
|
+
const paramsObj = {};
|
|
1254
|
+
if (limit) paramsObj.limit = limit;
|
|
1255
|
+
if (offset) paramsObj.offset = offset;
|
|
1256
|
+
return await this.makeRequest('GET', '/api/assets', paramsObj, {
|
|
1257
|
+
cache: false // Don't cache file lists - always get fresh data
|
|
1258
|
+
});
|
|
1112
1259
|
} catch (error) {
|
|
1113
1260
|
throw this.handleError(error);
|
|
1114
1261
|
}
|
|
@@ -1121,8 +1268,14 @@ export class OxyServices {
|
|
|
1121
1268
|
*/
|
|
1122
1269
|
async getFileContentAsText(fileId, variant) {
|
|
1123
1270
|
try {
|
|
1124
|
-
const
|
|
1125
|
-
|
|
1271
|
+
const params = variant ? {
|
|
1272
|
+
variant
|
|
1273
|
+
} : undefined;
|
|
1274
|
+
const urlRes = await this.makeRequest('GET', `/api/assets/${encodeURIComponent(fileId)}/url`, params, {
|
|
1275
|
+
cache: true,
|
|
1276
|
+
cacheTTL: 10 * 60 * 1000 // 10 minutes cache for URLs
|
|
1277
|
+
});
|
|
1278
|
+
const downloadUrl = urlRes?.url;
|
|
1126
1279
|
const response = await fetch(downloadUrl);
|
|
1127
1280
|
return await response.text();
|
|
1128
1281
|
} catch (error) {
|
|
@@ -1135,8 +1288,14 @@ export class OxyServices {
|
|
|
1135
1288
|
*/
|
|
1136
1289
|
async getFileContentAsBlob(fileId, variant) {
|
|
1137
1290
|
try {
|
|
1138
|
-
const
|
|
1139
|
-
|
|
1291
|
+
const params = variant ? {
|
|
1292
|
+
variant
|
|
1293
|
+
} : undefined;
|
|
1294
|
+
const urlRes = await this.makeRequest('GET', `/api/assets/${encodeURIComponent(fileId)}/url`, params, {
|
|
1295
|
+
cache: true,
|
|
1296
|
+
cacheTTL: 10 * 60 * 1000 // 10 minutes cache for URLs
|
|
1297
|
+
});
|
|
1298
|
+
const downloadUrl = urlRes?.url;
|
|
1140
1299
|
const response = await fetch(downloadUrl);
|
|
1141
1300
|
return await response.blob();
|
|
1142
1301
|
} catch (error) {
|
|
@@ -1171,12 +1330,13 @@ export class OxyServices {
|
|
|
1171
1330
|
*/
|
|
1172
1331
|
async assetInit(sha256, size, mime) {
|
|
1173
1332
|
try {
|
|
1174
|
-
|
|
1333
|
+
return await this.makeRequest('POST', '/api/assets/init', {
|
|
1175
1334
|
sha256,
|
|
1176
1335
|
size,
|
|
1177
1336
|
mime
|
|
1337
|
+
}, {
|
|
1338
|
+
cache: false
|
|
1178
1339
|
});
|
|
1179
|
-
return res.data;
|
|
1180
1340
|
} catch (error) {
|
|
1181
1341
|
throw this.handleError(error);
|
|
1182
1342
|
}
|
|
@@ -1187,15 +1347,16 @@ export class OxyServices {
|
|
|
1187
1347
|
*/
|
|
1188
1348
|
async assetComplete(fileId, originalName, size, mime, visibility, metadata) {
|
|
1189
1349
|
try {
|
|
1190
|
-
|
|
1350
|
+
return await this.makeRequest('POST', '/api/assets/complete', {
|
|
1191
1351
|
fileId,
|
|
1192
1352
|
originalName,
|
|
1193
1353
|
size,
|
|
1194
1354
|
mime,
|
|
1195
1355
|
visibility,
|
|
1196
1356
|
metadata
|
|
1357
|
+
}, {
|
|
1358
|
+
cache: false
|
|
1197
1359
|
});
|
|
1198
|
-
return res.data;
|
|
1199
1360
|
} catch (error) {
|
|
1200
1361
|
throw this.handleError(error);
|
|
1201
1362
|
}
|
|
@@ -1219,10 +1380,11 @@ export class OxyServices {
|
|
|
1219
1380
|
// Fallback: direct upload via API to avoid CORS issues
|
|
1220
1381
|
const fd = new FormData();
|
|
1221
1382
|
fd.append('file', file);
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
}
|
|
1383
|
+
// Use httpClient directly for FormData uploads (bypasses RequestManager for special handling)
|
|
1384
|
+
await this.httpClient.request({
|
|
1385
|
+
method: 'POST',
|
|
1386
|
+
url: `/api/assets/${encodeURIComponent(initResponse.fileId)}/upload-direct`,
|
|
1387
|
+
data: fd
|
|
1226
1388
|
});
|
|
1227
1389
|
}
|
|
1228
1390
|
|
|
@@ -1273,8 +1435,9 @@ export class OxyServices {
|
|
|
1273
1435
|
};
|
|
1274
1436
|
if (visibility) body.visibility = visibility;
|
|
1275
1437
|
if (webhookUrl) body.webhookUrl = webhookUrl;
|
|
1276
|
-
|
|
1277
|
-
|
|
1438
|
+
return await this.makeRequest('POST', `/api/assets/${fileId}/links`, body, {
|
|
1439
|
+
cache: false
|
|
1440
|
+
});
|
|
1278
1441
|
} catch (error) {
|
|
1279
1442
|
throw this.handleError(error);
|
|
1280
1443
|
}
|
|
@@ -1285,14 +1448,13 @@ export class OxyServices {
|
|
|
1285
1448
|
*/
|
|
1286
1449
|
async assetUnlink(fileId, app, entityType, entityId) {
|
|
1287
1450
|
try {
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1451
|
+
return await this.makeRequest('DELETE', `/api/assets/${fileId}/links`, {
|
|
1452
|
+
app,
|
|
1453
|
+
entityType,
|
|
1454
|
+
entityId
|
|
1455
|
+
}, {
|
|
1456
|
+
cache: false
|
|
1294
1457
|
});
|
|
1295
|
-
return res.data;
|
|
1296
1458
|
} catch (error) {
|
|
1297
1459
|
throw this.handleError(error);
|
|
1298
1460
|
}
|
|
@@ -1303,8 +1465,10 @@ export class OxyServices {
|
|
|
1303
1465
|
*/
|
|
1304
1466
|
async assetGet(fileId) {
|
|
1305
1467
|
try {
|
|
1306
|
-
|
|
1307
|
-
|
|
1468
|
+
return await this.makeRequest('GET', `/api/assets/${fileId}`, undefined, {
|
|
1469
|
+
cache: true,
|
|
1470
|
+
cacheTTL: 5 * 60 * 1000 // 5 minutes cache
|
|
1471
|
+
});
|
|
1308
1472
|
} catch (error) {
|
|
1309
1473
|
throw this.handleError(error);
|
|
1310
1474
|
}
|
|
@@ -1315,13 +1479,13 @@ export class OxyServices {
|
|
|
1315
1479
|
*/
|
|
1316
1480
|
async assetGetUrl(fileId, variant, expiresIn) {
|
|
1317
1481
|
try {
|
|
1318
|
-
const params =
|
|
1319
|
-
if (variant) params.
|
|
1320
|
-
if (expiresIn) params.
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1482
|
+
const params = {};
|
|
1483
|
+
if (variant) params.variant = variant;
|
|
1484
|
+
if (expiresIn) params.expiresIn = expiresIn;
|
|
1485
|
+
return await this.makeRequest('GET', `/api/assets/${fileId}/url`, params, {
|
|
1486
|
+
cache: true,
|
|
1487
|
+
cacheTTL: 10 * 60 * 1000 // 10 minutes cache for URLs
|
|
1488
|
+
});
|
|
1325
1489
|
} catch (error) {
|
|
1326
1490
|
throw this.handleError(error);
|
|
1327
1491
|
}
|
|
@@ -1332,8 +1496,9 @@ export class OxyServices {
|
|
|
1332
1496
|
*/
|
|
1333
1497
|
async assetRestore(fileId) {
|
|
1334
1498
|
try {
|
|
1335
|
-
|
|
1336
|
-
|
|
1499
|
+
return await this.makeRequest('POST', `/api/assets/${fileId}/restore`, undefined, {
|
|
1500
|
+
cache: false
|
|
1501
|
+
});
|
|
1337
1502
|
} catch (error) {
|
|
1338
1503
|
throw this.handleError(error);
|
|
1339
1504
|
}
|
|
@@ -1344,9 +1509,12 @@ export class OxyServices {
|
|
|
1344
1509
|
*/
|
|
1345
1510
|
async assetDelete(fileId, force = false) {
|
|
1346
1511
|
try {
|
|
1347
|
-
const params = force ?
|
|
1348
|
-
|
|
1349
|
-
|
|
1512
|
+
const params = force ? {
|
|
1513
|
+
force: 'true'
|
|
1514
|
+
} : undefined;
|
|
1515
|
+
return await this.makeRequest('DELETE', `/api/assets/${fileId}`, params, {
|
|
1516
|
+
cache: false
|
|
1517
|
+
});
|
|
1350
1518
|
} catch (error) {
|
|
1351
1519
|
throw this.handleError(error);
|
|
1352
1520
|
}
|
|
@@ -1372,10 +1540,11 @@ export class OxyServices {
|
|
|
1372
1540
|
*/
|
|
1373
1541
|
async assetUpdateVisibility(fileId, visibility) {
|
|
1374
1542
|
try {
|
|
1375
|
-
|
|
1543
|
+
return await this.makeRequest('PATCH', `/api/assets/${fileId}/visibility`, {
|
|
1376
1544
|
visibility
|
|
1545
|
+
}, {
|
|
1546
|
+
cache: false
|
|
1377
1547
|
});
|
|
1378
|
-
return res.data;
|
|
1379
1548
|
} catch (error) {
|
|
1380
1549
|
throw this.handleError(error);
|
|
1381
1550
|
}
|
|
@@ -1430,8 +1599,11 @@ export class OxyServices {
|
|
|
1430
1599
|
*/
|
|
1431
1600
|
async getDeveloperApps() {
|
|
1432
1601
|
try {
|
|
1433
|
-
const res = await this.
|
|
1434
|
-
|
|
1602
|
+
const res = await this.makeRequest('GET', '/api/developer/apps', undefined, {
|
|
1603
|
+
cache: true,
|
|
1604
|
+
cacheTTL: 2 * 60 * 1000 // 2 minutes cache
|
|
1605
|
+
});
|
|
1606
|
+
return res.apps || [];
|
|
1435
1607
|
} catch (error) {
|
|
1436
1608
|
throw this.handleError(error);
|
|
1437
1609
|
}
|
|
@@ -1442,8 +1614,10 @@ export class OxyServices {
|
|
|
1442
1614
|
*/
|
|
1443
1615
|
async createDeveloperApp(data) {
|
|
1444
1616
|
try {
|
|
1445
|
-
const res = await this.
|
|
1446
|
-
|
|
1617
|
+
const res = await this.makeRequest('POST', '/api/developer/apps', data, {
|
|
1618
|
+
cache: false
|
|
1619
|
+
});
|
|
1620
|
+
return res.app;
|
|
1447
1621
|
} catch (error) {
|
|
1448
1622
|
throw this.handleError(error);
|
|
1449
1623
|
}
|
|
@@ -1454,8 +1628,11 @@ export class OxyServices {
|
|
|
1454
1628
|
*/
|
|
1455
1629
|
async getDeveloperApp(appId) {
|
|
1456
1630
|
try {
|
|
1457
|
-
const res = await this.
|
|
1458
|
-
|
|
1631
|
+
const res = await this.makeRequest('GET', `/api/developer/apps/${appId}`, undefined, {
|
|
1632
|
+
cache: true,
|
|
1633
|
+
cacheTTL: 5 * 60 * 1000 // 5 minutes cache
|
|
1634
|
+
});
|
|
1635
|
+
return res.app;
|
|
1459
1636
|
} catch (error) {
|
|
1460
1637
|
throw this.handleError(error);
|
|
1461
1638
|
}
|
|
@@ -1466,8 +1643,10 @@ export class OxyServices {
|
|
|
1466
1643
|
*/
|
|
1467
1644
|
async updateDeveloperApp(appId, data) {
|
|
1468
1645
|
try {
|
|
1469
|
-
const res = await this.
|
|
1470
|
-
|
|
1646
|
+
const res = await this.makeRequest('PATCH', `/api/developer/apps/${appId}`, data, {
|
|
1647
|
+
cache: false
|
|
1648
|
+
});
|
|
1649
|
+
return res.app;
|
|
1471
1650
|
} catch (error) {
|
|
1472
1651
|
throw this.handleError(error);
|
|
1473
1652
|
}
|
|
@@ -1478,8 +1657,9 @@ export class OxyServices {
|
|
|
1478
1657
|
*/
|
|
1479
1658
|
async regenerateDeveloperAppSecret(appId) {
|
|
1480
1659
|
try {
|
|
1481
|
-
|
|
1482
|
-
|
|
1660
|
+
return await this.makeRequest('POST', `/api/developer/apps/${appId}/regenerate-secret`, undefined, {
|
|
1661
|
+
cache: false
|
|
1662
|
+
});
|
|
1483
1663
|
} catch (error) {
|
|
1484
1664
|
throw this.handleError(error);
|
|
1485
1665
|
}
|
|
@@ -1490,8 +1670,9 @@ export class OxyServices {
|
|
|
1490
1670
|
*/
|
|
1491
1671
|
async deleteDeveloperApp(appId) {
|
|
1492
1672
|
try {
|
|
1493
|
-
|
|
1494
|
-
|
|
1673
|
+
return await this.makeRequest('DELETE', `/api/developer/apps/${appId}`, undefined, {
|
|
1674
|
+
cache: false
|
|
1675
|
+
});
|
|
1495
1676
|
} catch (error) {
|
|
1496
1677
|
throw this.handleError(error);
|
|
1497
1678
|
}
|
|
@@ -1506,11 +1687,12 @@ export class OxyServices {
|
|
|
1506
1687
|
*/
|
|
1507
1688
|
async updateLocation(latitude, longitude) {
|
|
1508
1689
|
try {
|
|
1509
|
-
|
|
1690
|
+
return await this.makeRequest('POST', '/api/location', {
|
|
1510
1691
|
latitude,
|
|
1511
1692
|
longitude
|
|
1693
|
+
}, {
|
|
1694
|
+
cache: false
|
|
1512
1695
|
});
|
|
1513
|
-
return res.data;
|
|
1514
1696
|
} catch (error) {
|
|
1515
1697
|
throw this.handleError(error);
|
|
1516
1698
|
}
|
|
@@ -1521,9 +1703,12 @@ export class OxyServices {
|
|
|
1521
1703
|
*/
|
|
1522
1704
|
async getNearbyUsers(radius) {
|
|
1523
1705
|
try {
|
|
1524
|
-
const params = radius ?
|
|
1525
|
-
|
|
1526
|
-
|
|
1706
|
+
const params = radius ? {
|
|
1707
|
+
radius
|
|
1708
|
+
} : undefined;
|
|
1709
|
+
return await this.makeRequest('GET', '/api/location/nearby', params, {
|
|
1710
|
+
cache: false // Don't cache location data - always get fresh data
|
|
1711
|
+
});
|
|
1527
1712
|
} catch (error) {
|
|
1528
1713
|
throw this.handleError(error);
|
|
1529
1714
|
}
|
|
@@ -1538,10 +1723,13 @@ export class OxyServices {
|
|
|
1538
1723
|
*/
|
|
1539
1724
|
async trackEvent(eventName, properties) {
|
|
1540
1725
|
try {
|
|
1541
|
-
await this.
|
|
1726
|
+
await this.makeRequest('POST', '/api/analytics/events', {
|
|
1542
1727
|
event: eventName,
|
|
1543
1728
|
properties
|
|
1544
|
-
}
|
|
1729
|
+
}, {
|
|
1730
|
+
cache: false,
|
|
1731
|
+
retry: false
|
|
1732
|
+
}); // Don't retry analytics events
|
|
1545
1733
|
} catch (error) {
|
|
1546
1734
|
throw this.handleError(error);
|
|
1547
1735
|
}
|
|
@@ -1552,11 +1740,13 @@ export class OxyServices {
|
|
|
1552
1740
|
*/
|
|
1553
1741
|
async getAnalytics(startDate, endDate) {
|
|
1554
1742
|
try {
|
|
1555
|
-
const params =
|
|
1556
|
-
if (startDate) params.
|
|
1557
|
-
if (endDate) params.
|
|
1558
|
-
|
|
1559
|
-
|
|
1743
|
+
const params = {};
|
|
1744
|
+
if (startDate) params.startDate = startDate;
|
|
1745
|
+
if (endDate) params.endDate = endDate;
|
|
1746
|
+
return await this.makeRequest('GET', '/api/analytics', params, {
|
|
1747
|
+
cache: true,
|
|
1748
|
+
cacheTTL: 5 * 60 * 1000 // 5 minutes cache
|
|
1749
|
+
});
|
|
1560
1750
|
} catch (error) {
|
|
1561
1751
|
throw this.handleError(error);
|
|
1562
1752
|
}
|
|
@@ -1571,8 +1761,9 @@ export class OxyServices {
|
|
|
1571
1761
|
*/
|
|
1572
1762
|
async registerDevice(deviceData) {
|
|
1573
1763
|
try {
|
|
1574
|
-
|
|
1575
|
-
|
|
1764
|
+
return await this.makeRequest('POST', '/api/devices', deviceData, {
|
|
1765
|
+
cache: false
|
|
1766
|
+
});
|
|
1576
1767
|
} catch (error) {
|
|
1577
1768
|
throw this.handleError(error);
|
|
1578
1769
|
}
|
|
@@ -1583,8 +1774,9 @@ export class OxyServices {
|
|
|
1583
1774
|
*/
|
|
1584
1775
|
async getUserDevices() {
|
|
1585
1776
|
try {
|
|
1586
|
-
|
|
1587
|
-
|
|
1777
|
+
return await this.makeRequest('GET', '/api/devices', undefined, {
|
|
1778
|
+
cache: false // Don't cache device list - always get fresh data
|
|
1779
|
+
});
|
|
1588
1780
|
} catch (error) {
|
|
1589
1781
|
throw this.handleError(error);
|
|
1590
1782
|
}
|
|
@@ -1595,7 +1787,9 @@ export class OxyServices {
|
|
|
1595
1787
|
*/
|
|
1596
1788
|
async removeDevice(deviceId) {
|
|
1597
1789
|
try {
|
|
1598
|
-
await this.
|
|
1790
|
+
await this.makeRequest('DELETE', `/api/devices/${deviceId}`, undefined, {
|
|
1791
|
+
cache: false
|
|
1792
|
+
});
|
|
1599
1793
|
} catch (error) {
|
|
1600
1794
|
throw this.handleError(error);
|
|
1601
1795
|
}
|
|
@@ -1603,11 +1797,17 @@ export class OxyServices {
|
|
|
1603
1797
|
|
|
1604
1798
|
/**
|
|
1605
1799
|
* Get device sessions
|
|
1800
|
+
* Note: Not cached by default to ensure fresh data, but can be cached via makeRequest if needed
|
|
1606
1801
|
*/
|
|
1607
1802
|
async getDeviceSessions(sessionId) {
|
|
1608
1803
|
try {
|
|
1609
|
-
|
|
1610
|
-
|
|
1804
|
+
// Use makeRequest for consistent error handling and optional caching
|
|
1805
|
+
// Cache disabled by default to ensure fresh session data
|
|
1806
|
+
return await this.makeRequest('GET', `/api/session/device/sessions/${sessionId}`, undefined, {
|
|
1807
|
+
cache: false,
|
|
1808
|
+
// Don't cache sessions - always get fresh data
|
|
1809
|
+
deduplicate: true // Deduplicate concurrent requests for same sessionId
|
|
1810
|
+
});
|
|
1611
1811
|
} catch (error) {
|
|
1612
1812
|
throw this.handleError(error);
|
|
1613
1813
|
}
|
|
@@ -1621,8 +1821,13 @@ export class OxyServices {
|
|
|
1621
1821
|
const params = new URLSearchParams();
|
|
1622
1822
|
if (deviceId) params.append('deviceId', deviceId);
|
|
1623
1823
|
if (excludeCurrent) params.append('excludeCurrent', 'true');
|
|
1624
|
-
const
|
|
1625
|
-
|
|
1824
|
+
const urlParams = {};
|
|
1825
|
+
params.forEach((value, key) => {
|
|
1826
|
+
urlParams[key] = value;
|
|
1827
|
+
});
|
|
1828
|
+
return await this.makeRequest('POST', `/api/session/device/logout-all/${sessionId}`, urlParams, {
|
|
1829
|
+
cache: false
|
|
1830
|
+
});
|
|
1626
1831
|
} catch (error) {
|
|
1627
1832
|
throw this.handleError(error);
|
|
1628
1833
|
}
|
|
@@ -1633,10 +1838,11 @@ export class OxyServices {
|
|
|
1633
1838
|
*/
|
|
1634
1839
|
async updateDeviceName(sessionId, deviceName) {
|
|
1635
1840
|
try {
|
|
1636
|
-
|
|
1841
|
+
return await this.makeRequest('PUT', `/api/session/device/name/${sessionId}`, {
|
|
1637
1842
|
deviceName
|
|
1843
|
+
}, {
|
|
1844
|
+
cache: false
|
|
1638
1845
|
});
|
|
1639
|
-
return res.data;
|
|
1640
1846
|
} catch (error) {
|
|
1641
1847
|
throw this.handleError(error);
|
|
1642
1848
|
}
|
|
@@ -1651,8 +1857,12 @@ export class OxyServices {
|
|
|
1651
1857
|
*/
|
|
1652
1858
|
async fetchLinkMetadata(url) {
|
|
1653
1859
|
try {
|
|
1654
|
-
|
|
1655
|
-
|
|
1860
|
+
return await this.makeRequest('GET', '/api/link-metadata', {
|
|
1861
|
+
url
|
|
1862
|
+
}, {
|
|
1863
|
+
cache: true,
|
|
1864
|
+
cacheTTL: 30 * 60 * 1000 // 30 minutes cache for link metadata
|
|
1865
|
+
});
|
|
1656
1866
|
} catch (error) {
|
|
1657
1867
|
throw this.handleError(error);
|
|
1658
1868
|
}
|