@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
package/src/core/OxyServices.ts
CHANGED
|
@@ -56,7 +56,6 @@
|
|
|
56
56
|
*
|
|
57
57
|
* See method JSDoc for more details and options.
|
|
58
58
|
*/
|
|
59
|
-
import axios, { type AxiosInstance, type InternalAxiosRequestConfig } from 'axios';
|
|
60
59
|
import { jwtDecode } from 'jwt-decode';
|
|
61
60
|
import type {
|
|
62
61
|
OxyConfig as OxyConfigBase,
|
|
@@ -67,6 +66,8 @@ import type {
|
|
|
67
66
|
AssetUrlResponse,
|
|
68
67
|
AssetVariant
|
|
69
68
|
} from '../models/interfaces';
|
|
69
|
+
import { normalizeLanguageCode, getLanguageMetadata, getLanguageName, getNativeLanguageName } from '../utils/languageUtils';
|
|
70
|
+
import type { LanguageMetadata } from '../utils/languageUtils';
|
|
70
71
|
/**
|
|
71
72
|
* OxyConfig - Configuration for OxyServices
|
|
72
73
|
* @property baseURL - The Oxy API base URL (e.g., https://api.oxy.so)
|
|
@@ -78,6 +79,8 @@ export interface OxyConfig extends OxyConfigBase {
|
|
|
78
79
|
import type { SessionLoginResponse } from '../models/session';
|
|
79
80
|
import { handleHttpError } from '../utils/errorUtils';
|
|
80
81
|
import { buildSearchParams, buildPaginationParams, type PaginationParams } from '../utils/apiUtils';
|
|
82
|
+
import { HttpClient } from './HttpClient';
|
|
83
|
+
import { RequestManager, type RequestOptions } from './RequestManager';
|
|
81
84
|
|
|
82
85
|
interface JwtPayload {
|
|
83
86
|
exp?: number;
|
|
@@ -117,55 +120,18 @@ export class OxyAuthenticationTimeoutError extends OxyAuthenticationError {
|
|
|
117
120
|
* OxyServices - Unified client library for interacting with the Oxy API
|
|
118
121
|
*
|
|
119
122
|
* This class provides all API functionality in one simple, easy-to-use interface.
|
|
120
|
-
*
|
|
123
|
+
* Architecture:
|
|
124
|
+
* - HttpClient: Handles HTTP communication and authentication
|
|
125
|
+
* - RequestManager: Handles caching, deduplication, queuing, and retry
|
|
126
|
+
* - OxyServices: Provides high-level API methods
|
|
121
127
|
*/
|
|
122
|
-
// Centralized token store
|
|
123
|
-
class TokenStore {
|
|
124
|
-
private static instance: TokenStore;
|
|
125
|
-
private accessToken: string | null = null;
|
|
126
|
-
private refreshToken: string | null = null;
|
|
127
|
-
|
|
128
|
-
private constructor() {}
|
|
129
|
-
|
|
130
|
-
static getInstance(): TokenStore {
|
|
131
|
-
if (!TokenStore.instance) {
|
|
132
|
-
TokenStore.instance = new TokenStore();
|
|
133
|
-
}
|
|
134
|
-
return TokenStore.instance;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
setTokens(accessToken: string, refreshToken = ''): void {
|
|
138
|
-
this.accessToken = accessToken;
|
|
139
|
-
this.refreshToken = refreshToken;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
getAccessToken(): string | null {
|
|
143
|
-
return this.accessToken;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
getRefreshToken(): string | null {
|
|
147
|
-
return this.refreshToken;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
clearTokens(): void {
|
|
151
|
-
this.accessToken = null;
|
|
152
|
-
this.refreshToken = null;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
hasAccessToken(): boolean {
|
|
156
|
-
return !!this.accessToken;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
128
|
export class OxyServices {
|
|
161
|
-
|
|
162
|
-
private
|
|
129
|
+
private httpClient: HttpClient;
|
|
130
|
+
private requestManager: RequestManager;
|
|
163
131
|
private cloudURL: string;
|
|
132
|
+
private config: OxyConfig;
|
|
133
|
+
|
|
164
134
|
|
|
165
|
-
/**
|
|
166
|
-
* Creates a new instance of the OxyServices client
|
|
167
|
-
* @param config - Configuration for the client
|
|
168
|
-
*/
|
|
169
135
|
/**
|
|
170
136
|
* Creates a new instance of the OxyServices client
|
|
171
137
|
* @param config - Configuration for the client
|
|
@@ -173,99 +139,33 @@ export class OxyServices {
|
|
|
173
139
|
* config.cloudURL: Oxy Cloud URL (e.g., https://cloud.oxy.so)
|
|
174
140
|
*/
|
|
175
141
|
constructor(config: OxyConfig) {
|
|
176
|
-
this.
|
|
177
|
-
baseURL: config.baseURL,
|
|
178
|
-
timeout: 5000 // 5 second timeout
|
|
179
|
-
});
|
|
142
|
+
this.config = config;
|
|
180
143
|
this.cloudURL = config.cloudURL || OXY_CLOUD_URL;
|
|
181
|
-
|
|
182
|
-
|
|
144
|
+
|
|
145
|
+
// Initialize HTTP client (handles authentication and interceptors)
|
|
146
|
+
this.httpClient = new HttpClient(config);
|
|
147
|
+
|
|
148
|
+
// Initialize request manager (handles caching, deduplication, queuing, retry)
|
|
149
|
+
this.requestManager = new RequestManager(this.httpClient, config);
|
|
183
150
|
}
|
|
184
151
|
|
|
185
152
|
// Test-only utility to reset global tokens between jest tests
|
|
186
153
|
static __resetTokensForTests(): void {
|
|
187
|
-
|
|
188
|
-
TokenStore.getInstance().clearTokens();
|
|
189
|
-
} catch {}
|
|
154
|
+
HttpClient.__resetTokensForTests();
|
|
190
155
|
}
|
|
191
156
|
|
|
157
|
+
|
|
192
158
|
/**
|
|
193
|
-
*
|
|
159
|
+
* Make a request with all performance optimizations
|
|
160
|
+
* This is the main method for all API calls - ensures authentication and performance features
|
|
194
161
|
*/
|
|
195
|
-
private
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const accessToken = this.tokenStore.getAccessToken();
|
|
203
|
-
if (!accessToken) {
|
|
204
|
-
console.log('❌ Interceptor - No token available');
|
|
205
|
-
return req;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
try {
|
|
209
|
-
console.log('✅ Interceptor - Adding Authorization header');
|
|
210
|
-
|
|
211
|
-
const decoded = jwtDecode<JwtPayload>(accessToken);
|
|
212
|
-
const currentTime = Math.floor(Date.now() / 1000);
|
|
213
|
-
|
|
214
|
-
// If token expires in less than 60 seconds, refresh it
|
|
215
|
-
if (decoded.exp && decoded.exp - currentTime < 60) {
|
|
216
|
-
// For session-based tokens, get a new token from the session
|
|
217
|
-
if (decoded.sessionId) {
|
|
218
|
-
try {
|
|
219
|
-
// Create a new axios instance to avoid interceptor recursion
|
|
220
|
-
const refreshClient = axios.create({
|
|
221
|
-
baseURL: this.client.defaults.baseURL,
|
|
222
|
-
timeout: this.client.defaults.timeout
|
|
223
|
-
});
|
|
224
|
-
const res = await refreshClient.get(`/api/session/token/${decoded.sessionId}`);
|
|
225
|
-
this.tokenStore.setTokens(res.data.accessToken);
|
|
226
|
-
req.headers.Authorization = `Bearer ${res.data.accessToken}`;
|
|
227
|
-
console.log('✅ Interceptor - Token refreshed and Authorization header set');
|
|
228
|
-
} catch (refreshError) {
|
|
229
|
-
// If refresh fails, use current token anyway
|
|
230
|
-
req.headers.Authorization = `Bearer ${accessToken}`;
|
|
231
|
-
console.log('❌ Interceptor - Token refresh failed, using current token');
|
|
232
|
-
}
|
|
233
|
-
} else {
|
|
234
|
-
// No session ID, use current token
|
|
235
|
-
req.headers.Authorization = `Bearer ${accessToken}`;
|
|
236
|
-
console.log('✅ Interceptor - No session ID, using current token');
|
|
237
|
-
}
|
|
238
|
-
} else {
|
|
239
|
-
// Add authorization header with current token
|
|
240
|
-
req.headers.Authorization = `Bearer ${accessToken}`;
|
|
241
|
-
console.log('✅ Interceptor - Authorization header set with current token');
|
|
242
|
-
}
|
|
243
|
-
} catch (error) {
|
|
244
|
-
console.log('❌ Interceptor - Error processing token:', error);
|
|
245
|
-
// Even if there's an error, still try to use the token
|
|
246
|
-
req.headers.Authorization = `Bearer ${accessToken}`;
|
|
247
|
-
console.log('⚠️ Interceptor - Using token despite error');
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
return req;
|
|
251
|
-
},
|
|
252
|
-
(error) => {
|
|
253
|
-
console.log('❌ Interceptor - Request error:', error);
|
|
254
|
-
return Promise.reject(error);
|
|
255
|
-
}
|
|
256
|
-
);
|
|
257
|
-
|
|
258
|
-
// Response interceptor for handling auth errors
|
|
259
|
-
this.client.interceptors.response.use(
|
|
260
|
-
(response) => response,
|
|
261
|
-
(error) => {
|
|
262
|
-
if (error.response?.status === 401) {
|
|
263
|
-
console.log('❌ Response interceptor - 401 Unauthorized, clearing tokens');
|
|
264
|
-
this.clearTokens();
|
|
265
|
-
}
|
|
266
|
-
return Promise.reject(error);
|
|
267
|
-
}
|
|
268
|
-
);
|
|
162
|
+
private async makeRequest<T>(
|
|
163
|
+
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',
|
|
164
|
+
url: string,
|
|
165
|
+
data?: any,
|
|
166
|
+
options: RequestOptions = {}
|
|
167
|
+
): Promise<T> {
|
|
168
|
+
return this.requestManager.request<T>(method, url, data, options);
|
|
269
169
|
}
|
|
270
170
|
|
|
271
171
|
// ============================================================================
|
|
@@ -276,7 +176,35 @@ export class OxyServices {
|
|
|
276
176
|
* Get the configured Oxy API base URL
|
|
277
177
|
*/
|
|
278
178
|
public getBaseURL(): string {
|
|
279
|
-
return this.
|
|
179
|
+
return this.httpClient.getBaseURL();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Get performance metrics
|
|
184
|
+
*/
|
|
185
|
+
public getMetrics() {
|
|
186
|
+
return this.requestManager.getMetrics();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Clear request cache
|
|
191
|
+
*/
|
|
192
|
+
public clearCache(): void {
|
|
193
|
+
this.requestManager.clearCache();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Clear specific cache entry
|
|
198
|
+
*/
|
|
199
|
+
public clearCacheEntry(key: string): void {
|
|
200
|
+
this.requestManager.clearCacheEntry(key);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Get cache statistics
|
|
205
|
+
*/
|
|
206
|
+
public getCacheStats() {
|
|
207
|
+
return this.requestManager.getCacheStats();
|
|
280
208
|
}
|
|
281
209
|
|
|
282
210
|
/**
|
|
@@ -286,25 +214,33 @@ export class OxyServices {
|
|
|
286
214
|
return this.cloudURL;
|
|
287
215
|
}
|
|
288
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Get the underlying HTTP client instance
|
|
219
|
+
* Useful for advanced use cases that need direct access to the HttpClient
|
|
220
|
+
*/
|
|
221
|
+
public getClient(): HttpClient {
|
|
222
|
+
return this.httpClient;
|
|
223
|
+
}
|
|
224
|
+
|
|
289
225
|
/**
|
|
290
226
|
* Set authentication tokens
|
|
291
227
|
*/
|
|
292
228
|
public setTokens(accessToken: string, refreshToken = ''): void {
|
|
293
|
-
this.
|
|
229
|
+
this.httpClient.setTokens(accessToken, refreshToken);
|
|
294
230
|
}
|
|
295
231
|
|
|
296
232
|
/**
|
|
297
233
|
* Clear stored authentication tokens
|
|
298
234
|
*/
|
|
299
235
|
public clearTokens(): void {
|
|
300
|
-
this.
|
|
236
|
+
this.httpClient.clearTokens();
|
|
301
237
|
}
|
|
302
238
|
|
|
303
239
|
/**
|
|
304
240
|
* Get the current user ID from the access token
|
|
305
241
|
*/
|
|
306
242
|
public getCurrentUserId(): string | null {
|
|
307
|
-
const accessToken = this.
|
|
243
|
+
const accessToken = this.httpClient.getAccessToken();
|
|
308
244
|
if (!accessToken) {
|
|
309
245
|
return null;
|
|
310
246
|
}
|
|
@@ -321,21 +257,21 @@ export class OxyServices {
|
|
|
321
257
|
* Check if the client has a valid access token
|
|
322
258
|
*/
|
|
323
259
|
private hasAccessToken(): boolean {
|
|
324
|
-
return this.
|
|
260
|
+
return this.httpClient.hasAccessToken();
|
|
325
261
|
}
|
|
326
262
|
|
|
327
263
|
/**
|
|
328
264
|
* Check if the client has a valid access token (public method)
|
|
329
265
|
*/
|
|
330
266
|
public hasValidToken(): boolean {
|
|
331
|
-
return this.
|
|
267
|
+
return this.httpClient.hasAccessToken();
|
|
332
268
|
}
|
|
333
269
|
|
|
334
270
|
/**
|
|
335
271
|
* Get the raw access token (for constructing anchor URLs when needed)
|
|
336
272
|
*/
|
|
337
273
|
public getAccessToken(): string | null {
|
|
338
|
-
return this.
|
|
274
|
+
return this.httpClient.getAccessToken();
|
|
339
275
|
}
|
|
340
276
|
|
|
341
277
|
/**
|
|
@@ -354,7 +290,7 @@ export class OxyServices {
|
|
|
354
290
|
const checkInterval = 100; // Check every 100ms
|
|
355
291
|
|
|
356
292
|
while (Date.now() - startTime < timeoutMs) {
|
|
357
|
-
if (this.
|
|
293
|
+
if (this.httpClient.hasAccessToken()) {
|
|
358
294
|
return true;
|
|
359
295
|
}
|
|
360
296
|
await new Promise(resolve => setTimeout(resolve, checkInterval));
|
|
@@ -385,17 +321,14 @@ export class OxyServices {
|
|
|
385
321
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
386
322
|
try {
|
|
387
323
|
// First attempt: check if we have a token
|
|
388
|
-
if (!this.
|
|
324
|
+
if (!this.httpClient.hasAccessToken()) {
|
|
389
325
|
if (attempt === 0) {
|
|
390
326
|
// On first attempt, wait briefly for authentication to complete
|
|
391
|
-
console.log(`🔄 ${operationName} - Waiting for authentication...`);
|
|
392
327
|
const authReady = await this.waitForAuthentication(authTimeoutMs);
|
|
393
328
|
|
|
394
329
|
if (!authReady) {
|
|
395
330
|
throw new OxyAuthenticationTimeoutError(operationName, authTimeoutMs);
|
|
396
331
|
}
|
|
397
|
-
|
|
398
|
-
console.log(`✅ ${operationName} - Authentication ready, proceeding...`);
|
|
399
332
|
} else {
|
|
400
333
|
// On retry attempts, fail immediately if no token
|
|
401
334
|
throw new OxyAuthenticationError(
|
|
@@ -416,7 +349,6 @@ export class OxyServices {
|
|
|
416
349
|
error instanceof OxyAuthenticationError;
|
|
417
350
|
|
|
418
351
|
if (isAuthError && !isLastAttempt && !(error instanceof OxyAuthenticationTimeoutError)) {
|
|
419
|
-
console.log(`🔄 ${operationName} - Auth error on attempt ${attempt + 1}, retrying in ${retryDelay}ms...`);
|
|
420
352
|
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
|
421
353
|
continue;
|
|
422
354
|
}
|
|
@@ -442,19 +374,16 @@ export class OxyServices {
|
|
|
442
374
|
}
|
|
443
375
|
|
|
444
376
|
try {
|
|
445
|
-
const res = await this.
|
|
446
|
-
|
|
377
|
+
const res = await this.makeRequest<{ valid: boolean }>('GET', '/api/auth/validate', undefined, {
|
|
378
|
+
cache: false,
|
|
379
|
+
retry: false,
|
|
380
|
+
});
|
|
381
|
+
return res.valid === true;
|
|
447
382
|
} catch (error) {
|
|
448
383
|
return false;
|
|
449
384
|
}
|
|
450
385
|
}
|
|
451
386
|
|
|
452
|
-
/**
|
|
453
|
-
* Get the HTTP client instance (public for external use)
|
|
454
|
-
*/
|
|
455
|
-
public getClient(): AxiosInstance {
|
|
456
|
-
return this.client;
|
|
457
|
-
}
|
|
458
387
|
|
|
459
388
|
/**
|
|
460
389
|
* Centralized error handling
|
|
@@ -478,8 +407,7 @@ export class OxyServices {
|
|
|
478
407
|
[key: string]: any
|
|
479
408
|
}> {
|
|
480
409
|
try {
|
|
481
|
-
|
|
482
|
-
return res.data;
|
|
410
|
+
return await this.makeRequest('GET', '/health', undefined, { cache: false });
|
|
483
411
|
} catch (error) {
|
|
484
412
|
throw this.handleError(error);
|
|
485
413
|
}
|
|
@@ -494,15 +422,15 @@ export class OxyServices {
|
|
|
494
422
|
*/
|
|
495
423
|
async signUp(username: string, email: string, password: string): Promise<{ message: string; token: string; user: User }> {
|
|
496
424
|
try {
|
|
497
|
-
const res = await this.
|
|
425
|
+
const res = await this.makeRequest<{ message: string; token: string; user: User }>('POST', '/api/auth/signup', {
|
|
498
426
|
username,
|
|
499
427
|
email,
|
|
500
428
|
password
|
|
501
|
-
});
|
|
502
|
-
if (!res ||
|
|
429
|
+
}, { cache: false });
|
|
430
|
+
if (!res || (typeof res === 'object' && Object.keys(res).length === 0)) {
|
|
503
431
|
throw new OxyAuthenticationError('Sign up failed', 'SIGNUP_FAILED', 400);
|
|
504
432
|
}
|
|
505
|
-
return res
|
|
433
|
+
return res;
|
|
506
434
|
} catch (error) {
|
|
507
435
|
throw this.handleError(error);
|
|
508
436
|
}
|
|
@@ -513,8 +441,7 @@ export class OxyServices {
|
|
|
513
441
|
*/
|
|
514
442
|
async requestRecovery(identifier: string): Promise<{ delivery?: string; destination?: string }> {
|
|
515
443
|
try {
|
|
516
|
-
|
|
517
|
-
return res.data;
|
|
444
|
+
return await this.makeRequest('POST', '/api/auth/recover/request', { identifier }, { cache: false });
|
|
518
445
|
} catch (error: any) {
|
|
519
446
|
throw this.handleError(error);
|
|
520
447
|
}
|
|
@@ -525,8 +452,7 @@ export class OxyServices {
|
|
|
525
452
|
*/
|
|
526
453
|
async verifyRecoveryCode(identifier: string, code: string): Promise<{ verified: boolean }> {
|
|
527
454
|
try {
|
|
528
|
-
|
|
529
|
-
return res.data;
|
|
455
|
+
return await this.makeRequest('POST', '/api/auth/recover/verify', { identifier, code }, { cache: false });
|
|
530
456
|
} catch (error: any) {
|
|
531
457
|
throw this.handleError(error);
|
|
532
458
|
}
|
|
@@ -537,8 +463,7 @@ export class OxyServices {
|
|
|
537
463
|
*/
|
|
538
464
|
async resetPassword(identifier: string, code: string, newPassword: string): Promise<{ success: boolean }> {
|
|
539
465
|
try {
|
|
540
|
-
|
|
541
|
-
return res.data;
|
|
466
|
+
return await this.makeRequest('POST', '/api/auth/recover/reset', { identifier, code, newPassword }, { cache: false });
|
|
542
467
|
} catch (error: any) {
|
|
543
468
|
throw this.handleError(error);
|
|
544
469
|
}
|
|
@@ -549,8 +474,7 @@ export class OxyServices {
|
|
|
549
474
|
*/
|
|
550
475
|
async resetPasswordWithTotp(identifier: string, code: string, newPassword: string): Promise<{ success: boolean }> {
|
|
551
476
|
try {
|
|
552
|
-
|
|
553
|
-
return res.data;
|
|
477
|
+
return await this.makeRequest('POST', '/api/auth/recover/totp/reset', { identifier, code, newPassword }, { cache: false });
|
|
554
478
|
} catch (error: any) {
|
|
555
479
|
throw this.handleError(error);
|
|
556
480
|
}
|
|
@@ -558,8 +482,7 @@ export class OxyServices {
|
|
|
558
482
|
|
|
559
483
|
async resetPasswordWithBackupCode(identifier: string, backupCode: string, newPassword: string): Promise<{ success: boolean }> {
|
|
560
484
|
try {
|
|
561
|
-
|
|
562
|
-
return res.data;
|
|
485
|
+
return await this.makeRequest('POST', '/api/auth/recover/backup/reset', { identifier, backupCode, newPassword }, { cache: false });
|
|
563
486
|
} catch (error: any) {
|
|
564
487
|
throw this.handleError(error);
|
|
565
488
|
}
|
|
@@ -567,8 +490,7 @@ export class OxyServices {
|
|
|
567
490
|
|
|
568
491
|
async resetPasswordWithRecoveryKey(identifier: string, recoveryKey: string, newPassword: string): Promise<{ success: boolean; nextRecoveryKey?: string }> {
|
|
569
492
|
try {
|
|
570
|
-
|
|
571
|
-
return res.data;
|
|
493
|
+
return await this.makeRequest('POST', '/api/auth/recover/recovery-key/reset', { identifier, recoveryKey, newPassword }, { cache: false });
|
|
572
494
|
} catch (error: any) {
|
|
573
495
|
throw this.handleError(error);
|
|
574
496
|
}
|
|
@@ -584,13 +506,12 @@ export class OxyServices {
|
|
|
584
506
|
deviceFingerprint?: any
|
|
585
507
|
): Promise<SessionLoginResponse | { mfaRequired: true; mfaToken: string; expiresAt: string }> {
|
|
586
508
|
try {
|
|
587
|
-
|
|
509
|
+
return await this.makeRequest<SessionLoginResponse | { mfaRequired: true; mfaToken: string; expiresAt: string }>('POST', '/api/auth/login', {
|
|
588
510
|
username,
|
|
589
511
|
password,
|
|
590
512
|
deviceName,
|
|
591
513
|
deviceFingerprint
|
|
592
|
-
});
|
|
593
|
-
return res.data;
|
|
514
|
+
}, { cache: false });
|
|
594
515
|
} catch (error) {
|
|
595
516
|
throw this.handleError(error);
|
|
596
517
|
}
|
|
@@ -601,8 +522,7 @@ export class OxyServices {
|
|
|
601
522
|
*/
|
|
602
523
|
async verifyTotpLogin(mfaToken: string, code: string): Promise<SessionLoginResponse> {
|
|
603
524
|
try {
|
|
604
|
-
|
|
605
|
-
return res.data;
|
|
525
|
+
return await this.makeRequest<SessionLoginResponse>('POST', '/api/auth/totp/verify-login', { mfaToken, code }, { cache: false });
|
|
606
526
|
} catch (error) {
|
|
607
527
|
throw this.handleError(error);
|
|
608
528
|
}
|
|
@@ -613,8 +533,38 @@ export class OxyServices {
|
|
|
613
533
|
*/
|
|
614
534
|
async getUserBySession(sessionId: string): Promise<User> {
|
|
615
535
|
try {
|
|
616
|
-
|
|
617
|
-
|
|
536
|
+
return await this.makeRequest<User>('GET', `/api/session/user/${sessionId}`, undefined, {
|
|
537
|
+
cache: true,
|
|
538
|
+
cacheTTL: 2 * 60 * 1000, // 2 minutes cache for user data
|
|
539
|
+
});
|
|
540
|
+
} catch (error) {
|
|
541
|
+
throw this.handleError(error);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Batch get multiple user profiles by session IDs (optimized for account switching)
|
|
547
|
+
* Returns array of { sessionId, user } objects
|
|
548
|
+
*/
|
|
549
|
+
async getUsersBySessions(sessionIds: string[]): Promise<Array<{ sessionId: string; user: User | null }>> {
|
|
550
|
+
try {
|
|
551
|
+
if (!Array.isArray(sessionIds) || sessionIds.length === 0) {
|
|
552
|
+
return [];
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Deduplicate and sort sessionIds for consistent cache keys
|
|
556
|
+
const uniqueSessionIds = Array.from(new Set(sessionIds)).sort();
|
|
557
|
+
|
|
558
|
+
return await this.makeRequest<Array<{ sessionId: string; user: User | null }>>(
|
|
559
|
+
'POST',
|
|
560
|
+
'/api/session/users/batch',
|
|
561
|
+
{ sessionIds: uniqueSessionIds },
|
|
562
|
+
{
|
|
563
|
+
cache: true,
|
|
564
|
+
cacheTTL: 2 * 60 * 1000, // 2 minutes cache
|
|
565
|
+
deduplicate: true, // Important for batch requests
|
|
566
|
+
}
|
|
567
|
+
);
|
|
618
568
|
} catch (error) {
|
|
619
569
|
throw this.handleError(error);
|
|
620
570
|
}
|
|
@@ -625,19 +575,16 @@ export class OxyServices {
|
|
|
625
575
|
*/
|
|
626
576
|
async getTokenBySession(sessionId: string): Promise<{ accessToken: string; expiresAt: string }> {
|
|
627
577
|
try {
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
console.log('🔑 getTokenBySession - Token received:', !!accessToken);
|
|
578
|
+
const res = await this.makeRequest<{ accessToken: string; expiresAt: string }>('GET', `/api/session/token/${sessionId}`, undefined, {
|
|
579
|
+
cache: false,
|
|
580
|
+
retry: false,
|
|
581
|
+
});
|
|
633
582
|
|
|
634
583
|
// Set the token in the centralized token store
|
|
635
|
-
this.setTokens(accessToken);
|
|
636
|
-
console.log('🔑 getTokenBySession - Token set in store');
|
|
584
|
+
this.setTokens(res.accessToken);
|
|
637
585
|
|
|
638
|
-
return res
|
|
586
|
+
return res;
|
|
639
587
|
} catch (error) {
|
|
640
|
-
console.log('❌ getTokenBySession - Error:', error);
|
|
641
588
|
throw this.handleError(error);
|
|
642
589
|
}
|
|
643
590
|
}
|
|
@@ -647,8 +594,9 @@ export class OxyServices {
|
|
|
647
594
|
*/
|
|
648
595
|
async getSessionsBySessionId(sessionId: string): Promise<any[]> {
|
|
649
596
|
try {
|
|
650
|
-
|
|
651
|
-
|
|
597
|
+
return await this.makeRequest('GET', `/api/session/sessions/${sessionId}`, undefined, {
|
|
598
|
+
cache: false,
|
|
599
|
+
});
|
|
652
600
|
} catch (error) {
|
|
653
601
|
throw this.handleError(error);
|
|
654
602
|
}
|
|
@@ -663,7 +611,7 @@ export class OxyServices {
|
|
|
663
611
|
? `/api/session/logout/${sessionId}/${targetSessionId}`
|
|
664
612
|
: `/api/session/logout/${sessionId}`;
|
|
665
613
|
|
|
666
|
-
await this.
|
|
614
|
+
await this.makeRequest('POST', url, undefined, { cache: false });
|
|
667
615
|
} catch (error) {
|
|
668
616
|
throw this.handleError(error);
|
|
669
617
|
}
|
|
@@ -674,7 +622,7 @@ export class OxyServices {
|
|
|
674
622
|
*/
|
|
675
623
|
async logoutAllSessions(sessionId: string): Promise<void> {
|
|
676
624
|
try {
|
|
677
|
-
await this.
|
|
625
|
+
await this.makeRequest('POST', `/api/session/logout-all/${sessionId}`, undefined, { cache: false });
|
|
678
626
|
} catch (error) {
|
|
679
627
|
throw this.handleError(error);
|
|
680
628
|
}
|
|
@@ -706,9 +654,11 @@ export class OxyServices {
|
|
|
706
654
|
params.append('useHeaderValidation', 'true');
|
|
707
655
|
}
|
|
708
656
|
|
|
709
|
-
const url = `/api/session/validate/${sessionId}
|
|
710
|
-
const
|
|
711
|
-
|
|
657
|
+
const url = `/api/session/validate/${sessionId}`;
|
|
658
|
+
const urlParams: any = {};
|
|
659
|
+
if (options.deviceFingerprint) urlParams.deviceFingerprint = options.deviceFingerprint;
|
|
660
|
+
if (options.useHeaderValidation) urlParams.useHeaderValidation = 'true';
|
|
661
|
+
return await this.makeRequest('GET', url, urlParams, { cache: false });
|
|
712
662
|
} catch (error) {
|
|
713
663
|
throw this.handleError(error);
|
|
714
664
|
}
|
|
@@ -719,8 +669,7 @@ export class OxyServices {
|
|
|
719
669
|
*/
|
|
720
670
|
async checkUsernameAvailability(username: string): Promise<{ available: boolean; message: string }> {
|
|
721
671
|
try {
|
|
722
|
-
|
|
723
|
-
return res.data;
|
|
672
|
+
return await this.makeRequest('GET', `/api/auth/check-username/${username}`, undefined, { cache: false });
|
|
724
673
|
} catch (error) {
|
|
725
674
|
throw this.handleError(error);
|
|
726
675
|
}
|
|
@@ -731,8 +680,7 @@ export class OxyServices {
|
|
|
731
680
|
*/
|
|
732
681
|
async checkEmailAvailability(email: string): Promise<{ available: boolean; message: string }> {
|
|
733
682
|
try {
|
|
734
|
-
|
|
735
|
-
return res.data;
|
|
683
|
+
return await this.makeRequest('GET', `/api/auth/check-email/${email}`, undefined, { cache: false });
|
|
736
684
|
} catch (error) {
|
|
737
685
|
throw this.handleError(error);
|
|
738
686
|
}
|
|
@@ -747,8 +695,10 @@ export class OxyServices {
|
|
|
747
695
|
*/
|
|
748
696
|
async getProfileByUsername(username: string): Promise<User> {
|
|
749
697
|
try {
|
|
750
|
-
|
|
751
|
-
|
|
698
|
+
return await this.makeRequest<User>('GET', `/api/profiles/username/${username}`, undefined, {
|
|
699
|
+
cache: true,
|
|
700
|
+
cacheTTL: 5 * 60 * 1000, // 5 minutes cache for profiles
|
|
701
|
+
});
|
|
752
702
|
} catch (error) {
|
|
753
703
|
throw this.handleError(error);
|
|
754
704
|
}
|
|
@@ -760,10 +710,8 @@ export class OxyServices {
|
|
|
760
710
|
|
|
761
711
|
async startTotpEnrollment(sessionId: string): Promise<{ secret: string; otpauthUrl: string; issuer: string; label: string }> {
|
|
762
712
|
try {
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
});
|
|
766
|
-
return res.data;
|
|
713
|
+
// Note: x-session-id header is handled by HttpClient interceptors if needed
|
|
714
|
+
return await this.makeRequest('POST', '/api/auth/totp/enroll/start', { sessionId }, { cache: false });
|
|
767
715
|
} catch (error) {
|
|
768
716
|
throw this.handleError(error);
|
|
769
717
|
}
|
|
@@ -771,10 +719,7 @@ export class OxyServices {
|
|
|
771
719
|
|
|
772
720
|
async verifyTotpEnrollment(sessionId: string, code: string): Promise<{ enabled: boolean; backupCodes?: string[]; recoveryKey?: string }> {
|
|
773
721
|
try {
|
|
774
|
-
|
|
775
|
-
headers: { 'x-session-id': sessionId }
|
|
776
|
-
});
|
|
777
|
-
return res.data;
|
|
722
|
+
return await this.makeRequest('POST', '/api/auth/totp/enroll/verify', { sessionId, code }, { cache: false });
|
|
778
723
|
} catch (error) {
|
|
779
724
|
throw this.handleError(error);
|
|
780
725
|
}
|
|
@@ -782,10 +727,7 @@ export class OxyServices {
|
|
|
782
727
|
|
|
783
728
|
async disableTotp(sessionId: string, code: string): Promise<{ disabled: boolean }> {
|
|
784
729
|
try {
|
|
785
|
-
|
|
786
|
-
headers: { 'x-session-id': sessionId }
|
|
787
|
-
});
|
|
788
|
-
return res.data;
|
|
730
|
+
return await this.makeRequest('POST', '/api/auth/totp/disable', { sessionId, code }, { cache: false });
|
|
789
731
|
} catch (error) {
|
|
790
732
|
throw this.handleError(error);
|
|
791
733
|
}
|
|
@@ -798,9 +740,14 @@ export class OxyServices {
|
|
|
798
740
|
try {
|
|
799
741
|
const params = { query, ...pagination };
|
|
800
742
|
const searchParams = buildSearchParams(params);
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
743
|
+
const paramsObj: any = {};
|
|
744
|
+
searchParams.forEach((value, key) => {
|
|
745
|
+
paramsObj[key] = value;
|
|
746
|
+
});
|
|
747
|
+
return await this.makeRequest<User[]>('GET', '/api/profiles/search', paramsObj, {
|
|
748
|
+
cache: true,
|
|
749
|
+
cacheTTL: 2 * 60 * 1000, // 2 minutes cache
|
|
750
|
+
});
|
|
804
751
|
} catch (error) {
|
|
805
752
|
throw this.handleError(error);
|
|
806
753
|
}
|
|
@@ -818,8 +765,7 @@ export class OxyServices {
|
|
|
818
765
|
[key: string]: any;
|
|
819
766
|
}>> {
|
|
820
767
|
return this.withAuthRetry(async () => {
|
|
821
|
-
|
|
822
|
-
return res.data;
|
|
768
|
+
return await this.makeRequest('GET', '/api/profiles/recommendations', undefined, { cache: true });
|
|
823
769
|
}, 'getProfileRecommendations');
|
|
824
770
|
}
|
|
825
771
|
|
|
@@ -828,8 +774,10 @@ export class OxyServices {
|
|
|
828
774
|
*/
|
|
829
775
|
async getUserById(userId: string): Promise<User> {
|
|
830
776
|
try {
|
|
831
|
-
|
|
832
|
-
|
|
777
|
+
return await this.makeRequest<User>('GET', `/api/users/${userId}`, undefined, {
|
|
778
|
+
cache: true,
|
|
779
|
+
cacheTTL: 5 * 60 * 1000, // 5 minutes cache
|
|
780
|
+
});
|
|
833
781
|
} catch (error) {
|
|
834
782
|
throw this.handleError(error);
|
|
835
783
|
}
|
|
@@ -840,8 +788,10 @@ export class OxyServices {
|
|
|
840
788
|
*/
|
|
841
789
|
async getCurrentUser(): Promise<User> {
|
|
842
790
|
return this.withAuthRetry(async () => {
|
|
843
|
-
|
|
844
|
-
|
|
791
|
+
return await this.makeRequest<User>('GET', '/api/users/me', undefined, {
|
|
792
|
+
cache: true,
|
|
793
|
+
cacheTTL: 1 * 60 * 1000, // 1 minute cache for current user
|
|
794
|
+
});
|
|
845
795
|
}, 'getCurrentUser');
|
|
846
796
|
}
|
|
847
797
|
|
|
@@ -850,20 +800,136 @@ export class OxyServices {
|
|
|
850
800
|
*/
|
|
851
801
|
async updateProfile(updates: Record<string, any>): Promise<User> {
|
|
852
802
|
try {
|
|
853
|
-
|
|
854
|
-
return res.data;
|
|
803
|
+
return await this.makeRequest<User>('PUT', '/api/users/me', updates, { cache: false });
|
|
855
804
|
} catch (error) {
|
|
856
805
|
throw this.handleError(error);
|
|
857
806
|
}
|
|
858
807
|
}
|
|
859
808
|
|
|
809
|
+
// ============================================================================
|
|
810
|
+
// LANGUAGE METHODS
|
|
811
|
+
// ============================================================================
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Get the current language from storage or user profile
|
|
815
|
+
* @param storageKeyPrefix - Optional prefix for storage key (default: 'oxy_session')
|
|
816
|
+
* @returns The current language code (e.g., 'en-US') or null if not set
|
|
817
|
+
*/
|
|
818
|
+
async getCurrentLanguage(storageKeyPrefix: string = 'oxy_session'): Promise<string | null> {
|
|
819
|
+
try {
|
|
820
|
+
// First try to get from user profile if authenticated
|
|
821
|
+
try {
|
|
822
|
+
const user = await this.getCurrentUser();
|
|
823
|
+
const userLanguage = (user as Record<string, unknown>)?.language as string | undefined;
|
|
824
|
+
if (userLanguage) {
|
|
825
|
+
return normalizeLanguageCode(userLanguage) || userLanguage;
|
|
826
|
+
}
|
|
827
|
+
} catch (e) {
|
|
828
|
+
// User not authenticated or error, continue to storage
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// Fall back to storage
|
|
832
|
+
const storage = await this.getStorage();
|
|
833
|
+
const storageKey = `${storageKeyPrefix}_language`;
|
|
834
|
+
const storedLanguage = await storage.getItem(storageKey);
|
|
835
|
+
if (storedLanguage) {
|
|
836
|
+
return normalizeLanguageCode(storedLanguage) || storedLanguage;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
return null;
|
|
840
|
+
} catch (error) {
|
|
841
|
+
if (__DEV__) {
|
|
842
|
+
console.warn('Failed to get current language:', error);
|
|
843
|
+
}
|
|
844
|
+
return null;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/**
|
|
849
|
+
* Get the current language with metadata (name, nativeName, etc.)
|
|
850
|
+
* @param storageKeyPrefix - Optional prefix for storage key (default: 'oxy_session')
|
|
851
|
+
* @returns Language metadata object or null if not set
|
|
852
|
+
*/
|
|
853
|
+
async getCurrentLanguageMetadata(storageKeyPrefix: string = 'oxy_session'): Promise<LanguageMetadata | null> {
|
|
854
|
+
const languageCode = await this.getCurrentLanguage(storageKeyPrefix);
|
|
855
|
+
return getLanguageMetadata(languageCode);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* Get the current language name (e.g., 'English')
|
|
860
|
+
* @param storageKeyPrefix - Optional prefix for storage key (default: 'oxy_session')
|
|
861
|
+
* @returns Language name or null if not set
|
|
862
|
+
*/
|
|
863
|
+
async getCurrentLanguageName(storageKeyPrefix: string = 'oxy_session'): Promise<string | null> {
|
|
864
|
+
const languageCode = await this.getCurrentLanguage(storageKeyPrefix);
|
|
865
|
+
if (!languageCode) return null;
|
|
866
|
+
return getLanguageName(languageCode);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* Get the current native language name (e.g., 'Español')
|
|
871
|
+
* @param storageKeyPrefix - Optional prefix for storage key (default: 'oxy_session')
|
|
872
|
+
* @returns Native language name or null if not set
|
|
873
|
+
*/
|
|
874
|
+
async getCurrentNativeLanguageName(storageKeyPrefix: string = 'oxy_session'): Promise<string | null> {
|
|
875
|
+
const languageCode = await this.getCurrentLanguage(storageKeyPrefix);
|
|
876
|
+
if (!languageCode) return null;
|
|
877
|
+
return getNativeLanguageName(languageCode);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* Get appropriate storage for the platform (similar to DeviceManager)
|
|
882
|
+
* @private
|
|
883
|
+
*/
|
|
884
|
+
private async getStorage(): Promise<{
|
|
885
|
+
getItem: (key: string) => Promise<string | null>;
|
|
886
|
+
setItem: (key: string, value: string) => Promise<void>;
|
|
887
|
+
removeItem: (key: string) => Promise<void>;
|
|
888
|
+
}> {
|
|
889
|
+
const isReactNative = typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
|
|
890
|
+
|
|
891
|
+
if (isReactNative) {
|
|
892
|
+
try {
|
|
893
|
+
const asyncStorageModule = await import('@react-native-async-storage/async-storage');
|
|
894
|
+
const storage = (asyncStorageModule.default as unknown) as import('@react-native-async-storage/async-storage').AsyncStorageStatic;
|
|
895
|
+
return {
|
|
896
|
+
getItem: storage.getItem.bind(storage),
|
|
897
|
+
setItem: storage.setItem.bind(storage),
|
|
898
|
+
removeItem: storage.removeItem.bind(storage),
|
|
899
|
+
};
|
|
900
|
+
} catch (error) {
|
|
901
|
+
console.error('AsyncStorage not available in React Native:', error);
|
|
902
|
+
throw new Error('AsyncStorage is required in React Native environment');
|
|
903
|
+
}
|
|
904
|
+
} else {
|
|
905
|
+
// Use localStorage for web
|
|
906
|
+
return {
|
|
907
|
+
getItem: async (key: string) => {
|
|
908
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
909
|
+
return localStorage.getItem(key);
|
|
910
|
+
}
|
|
911
|
+
return null;
|
|
912
|
+
},
|
|
913
|
+
setItem: async (key: string, value: string) => {
|
|
914
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
915
|
+
localStorage.setItem(key, value);
|
|
916
|
+
}
|
|
917
|
+
},
|
|
918
|
+
removeItem: async (key: string) => {
|
|
919
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
920
|
+
localStorage.removeItem(key);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
860
927
|
/**
|
|
861
928
|
* Update user by ID (admin function)
|
|
862
929
|
*/
|
|
863
930
|
async updateUser(userId: string, updates: Record<string, any>): Promise<User> {
|
|
864
931
|
try {
|
|
865
|
-
|
|
866
|
-
return res.data;
|
|
932
|
+
return await this.makeRequest<User>('PUT', `/api/users/${userId}`, updates, { cache: false });
|
|
867
933
|
} catch (error) {
|
|
868
934
|
throw this.handleError(error);
|
|
869
935
|
}
|
|
@@ -874,8 +940,7 @@ export class OxyServices {
|
|
|
874
940
|
*/
|
|
875
941
|
async followUser(userId: string): Promise<{ success: boolean; message: string }> {
|
|
876
942
|
try {
|
|
877
|
-
|
|
878
|
-
return res.data;
|
|
943
|
+
return await this.makeRequest('POST', `/api/users/${userId}/follow`, undefined, { cache: false });
|
|
879
944
|
} catch (error) {
|
|
880
945
|
throw this.handleError(error);
|
|
881
946
|
}
|
|
@@ -886,8 +951,7 @@ export class OxyServices {
|
|
|
886
951
|
*/
|
|
887
952
|
async unfollowUser(userId: string): Promise<{ success: boolean; message: string }> {
|
|
888
953
|
try {
|
|
889
|
-
|
|
890
|
-
return res.data;
|
|
954
|
+
return await this.makeRequest('DELETE', `/api/users/${userId}/follow`, undefined, { cache: false });
|
|
891
955
|
} catch (error) {
|
|
892
956
|
throw this.handleError(error);
|
|
893
957
|
}
|
|
@@ -898,8 +962,10 @@ export class OxyServices {
|
|
|
898
962
|
*/
|
|
899
963
|
async getFollowStatus(userId: string): Promise<{ isFollowing: boolean }> {
|
|
900
964
|
try {
|
|
901
|
-
|
|
902
|
-
|
|
965
|
+
return await this.makeRequest('GET', `/api/users/${userId}/follow-status`, undefined, {
|
|
966
|
+
cache: true,
|
|
967
|
+
cacheTTL: 1 * 60 * 1000, // 1 minute cache
|
|
968
|
+
});
|
|
903
969
|
} catch (error) {
|
|
904
970
|
throw this.handleError(error);
|
|
905
971
|
}
|
|
@@ -914,8 +980,15 @@ export class OxyServices {
|
|
|
914
980
|
): Promise<{ followers: User[]; total: number; hasMore: boolean }> {
|
|
915
981
|
try {
|
|
916
982
|
const params = buildPaginationParams(pagination || {});
|
|
917
|
-
const
|
|
918
|
-
|
|
983
|
+
const response = await this.makeRequest<{ data: User[]; pagination: { total: number; hasMore: boolean } }>('GET', `/api/users/${userId}/followers`, params, {
|
|
984
|
+
cache: true,
|
|
985
|
+
cacheTTL: 2 * 60 * 1000, // 2 minutes cache
|
|
986
|
+
});
|
|
987
|
+
return {
|
|
988
|
+
followers: response.data || [],
|
|
989
|
+
total: response.pagination.total,
|
|
990
|
+
hasMore: response.pagination.hasMore,
|
|
991
|
+
};
|
|
919
992
|
} catch (error) {
|
|
920
993
|
throw this.handleError(error);
|
|
921
994
|
}
|
|
@@ -930,8 +1003,15 @@ export class OxyServices {
|
|
|
930
1003
|
): Promise<{ following: User[]; total: number; hasMore: boolean }> {
|
|
931
1004
|
try {
|
|
932
1005
|
const params = buildPaginationParams(pagination || {});
|
|
933
|
-
const
|
|
934
|
-
|
|
1006
|
+
const response = await this.makeRequest<{ data: User[]; pagination: { total: number; hasMore: boolean } }>('GET', `/api/users/${userId}/following`, params, {
|
|
1007
|
+
cache: true,
|
|
1008
|
+
cacheTTL: 2 * 60 * 1000, // 2 minutes cache
|
|
1009
|
+
});
|
|
1010
|
+
return {
|
|
1011
|
+
following: response.data || [],
|
|
1012
|
+
total: response.pagination.total,
|
|
1013
|
+
hasMore: response.pagination.hasMore,
|
|
1014
|
+
};
|
|
935
1015
|
} catch (error) {
|
|
936
1016
|
throw this.handleError(error);
|
|
937
1017
|
}
|
|
@@ -942,8 +1022,9 @@ export class OxyServices {
|
|
|
942
1022
|
*/
|
|
943
1023
|
async getNotifications(): Promise<Notification[]> {
|
|
944
1024
|
return this.withAuthRetry(async () => {
|
|
945
|
-
|
|
946
|
-
|
|
1025
|
+
return await this.makeRequest<Notification[]>('GET', '/api/notifications', undefined, {
|
|
1026
|
+
cache: false, // Don't cache notifications - always get fresh data
|
|
1027
|
+
});
|
|
947
1028
|
}, 'getNotifications');
|
|
948
1029
|
}
|
|
949
1030
|
|
|
@@ -952,8 +1033,10 @@ export class OxyServices {
|
|
|
952
1033
|
*/
|
|
953
1034
|
async getUnreadCount(): Promise<number> {
|
|
954
1035
|
try {
|
|
955
|
-
const res = await this.
|
|
956
|
-
|
|
1036
|
+
const res = await this.makeRequest<{ count: number }>('GET', '/api/notifications/unread-count', undefined, {
|
|
1037
|
+
cache: false, // Don't cache unread count - always get fresh data
|
|
1038
|
+
});
|
|
1039
|
+
return res.count;
|
|
957
1040
|
} catch (error) {
|
|
958
1041
|
throw this.handleError(error);
|
|
959
1042
|
}
|
|
@@ -964,8 +1047,7 @@ export class OxyServices {
|
|
|
964
1047
|
*/
|
|
965
1048
|
async createNotification(data: Partial<Notification>): Promise<Notification> {
|
|
966
1049
|
try {
|
|
967
|
-
|
|
968
|
-
return res.data;
|
|
1050
|
+
return await this.makeRequest<Notification>('POST', '/api/notifications', data, { cache: false });
|
|
969
1051
|
} catch (error) {
|
|
970
1052
|
throw this.handleError(error);
|
|
971
1053
|
}
|
|
@@ -976,7 +1058,7 @@ export class OxyServices {
|
|
|
976
1058
|
*/
|
|
977
1059
|
async markNotificationAsRead(notificationId: string): Promise<void> {
|
|
978
1060
|
try {
|
|
979
|
-
await this.
|
|
1061
|
+
await this.makeRequest('PUT', `/api/notifications/${notificationId}/read`, undefined, { cache: false });
|
|
980
1062
|
} catch (error) {
|
|
981
1063
|
throw this.handleError(error);
|
|
982
1064
|
}
|
|
@@ -987,7 +1069,7 @@ export class OxyServices {
|
|
|
987
1069
|
*/
|
|
988
1070
|
async markAllNotificationsAsRead(): Promise<void> {
|
|
989
1071
|
try {
|
|
990
|
-
await this.
|
|
1072
|
+
await this.makeRequest('PUT', '/api/notifications/read-all', undefined, { cache: false });
|
|
991
1073
|
} catch (error) {
|
|
992
1074
|
throw this.handleError(error);
|
|
993
1075
|
}
|
|
@@ -998,7 +1080,7 @@ export class OxyServices {
|
|
|
998
1080
|
*/
|
|
999
1081
|
async deleteNotification(notificationId: string): Promise<void> {
|
|
1000
1082
|
try {
|
|
1001
|
-
await this.
|
|
1083
|
+
await this.makeRequest('DELETE', `/api/notifications/${notificationId}`, undefined, { cache: false });
|
|
1002
1084
|
} catch (error) {
|
|
1003
1085
|
throw this.handleError(error);
|
|
1004
1086
|
}
|
|
@@ -1013,8 +1095,7 @@ export class OxyServices {
|
|
|
1013
1095
|
*/
|
|
1014
1096
|
async createPayment(data: any): Promise<any> {
|
|
1015
1097
|
try {
|
|
1016
|
-
|
|
1017
|
-
return res.data;
|
|
1098
|
+
return await this.makeRequest('POST', '/api/payments', data, { cache: false });
|
|
1018
1099
|
} catch (error) {
|
|
1019
1100
|
throw this.handleError(error);
|
|
1020
1101
|
}
|
|
@@ -1025,8 +1106,10 @@ export class OxyServices {
|
|
|
1025
1106
|
*/
|
|
1026
1107
|
async getPayment(paymentId: string): Promise<any> {
|
|
1027
1108
|
try {
|
|
1028
|
-
|
|
1029
|
-
|
|
1109
|
+
return await this.makeRequest('GET', `/api/payments/${paymentId}`, undefined, {
|
|
1110
|
+
cache: true,
|
|
1111
|
+
cacheTTL: 5 * 60 * 1000, // 5 minutes cache
|
|
1112
|
+
});
|
|
1030
1113
|
} catch (error) {
|
|
1031
1114
|
throw this.handleError(error);
|
|
1032
1115
|
}
|
|
@@ -1037,8 +1120,9 @@ export class OxyServices {
|
|
|
1037
1120
|
*/
|
|
1038
1121
|
async getUserPayments(): Promise<any[]> {
|
|
1039
1122
|
try {
|
|
1040
|
-
|
|
1041
|
-
|
|
1123
|
+
return await this.makeRequest('GET', '/api/payments/user', undefined, {
|
|
1124
|
+
cache: false, // Don't cache user payments - always get fresh data
|
|
1125
|
+
});
|
|
1042
1126
|
} catch (error) {
|
|
1043
1127
|
throw this.handleError(error);
|
|
1044
1128
|
}
|
|
@@ -1053,8 +1137,10 @@ export class OxyServices {
|
|
|
1053
1137
|
*/
|
|
1054
1138
|
async getUserKarma(userId: string): Promise<any> {
|
|
1055
1139
|
try {
|
|
1056
|
-
|
|
1057
|
-
|
|
1140
|
+
return await this.makeRequest('GET', `/api/karma/${userId}`, undefined, {
|
|
1141
|
+
cache: true,
|
|
1142
|
+
cacheTTL: 2 * 60 * 1000, // 2 minutes cache
|
|
1143
|
+
});
|
|
1058
1144
|
} catch (error) {
|
|
1059
1145
|
throw this.handleError(error);
|
|
1060
1146
|
}
|
|
@@ -1065,11 +1151,10 @@ export class OxyServices {
|
|
|
1065
1151
|
*/
|
|
1066
1152
|
async giveKarma(userId: string, amount: number, reason?: string): Promise<any> {
|
|
1067
1153
|
try {
|
|
1068
|
-
|
|
1154
|
+
return await this.makeRequest('POST', `/api/karma/${userId}/give`, {
|
|
1069
1155
|
amount,
|
|
1070
1156
|
reason
|
|
1071
|
-
});
|
|
1072
|
-
return res.data;
|
|
1157
|
+
}, { cache: false });
|
|
1073
1158
|
} catch (error) {
|
|
1074
1159
|
throw this.handleError(error);
|
|
1075
1160
|
}
|
|
@@ -1080,8 +1165,10 @@ export class OxyServices {
|
|
|
1080
1165
|
*/
|
|
1081
1166
|
async getUserKarmaTotal(userId: string): Promise<any> {
|
|
1082
1167
|
try {
|
|
1083
|
-
|
|
1084
|
-
|
|
1168
|
+
return await this.makeRequest('GET', `/api/karma/${userId}/total`, undefined, {
|
|
1169
|
+
cache: true,
|
|
1170
|
+
cacheTTL: 2 * 60 * 1000, // 2 minutes cache
|
|
1171
|
+
});
|
|
1085
1172
|
} catch (error) {
|
|
1086
1173
|
throw this.handleError(error);
|
|
1087
1174
|
}
|
|
@@ -1092,12 +1179,14 @@ export class OxyServices {
|
|
|
1092
1179
|
*/
|
|
1093
1180
|
async getUserKarmaHistory(userId: string, limit?: number, offset?: number): Promise<any> {
|
|
1094
1181
|
try {
|
|
1095
|
-
const params =
|
|
1096
|
-
if (limit) params.
|
|
1097
|
-
if (offset) params.
|
|
1182
|
+
const params: any = {};
|
|
1183
|
+
if (limit) params.limit = limit;
|
|
1184
|
+
if (offset) params.offset = offset;
|
|
1098
1185
|
|
|
1099
|
-
|
|
1100
|
-
|
|
1186
|
+
return await this.makeRequest('GET', `/api/karma/${userId}/history`, params, {
|
|
1187
|
+
cache: true,
|
|
1188
|
+
cacheTTL: 2 * 60 * 1000, // 2 minutes cache
|
|
1189
|
+
});
|
|
1101
1190
|
} catch (error) {
|
|
1102
1191
|
throw this.handleError(error);
|
|
1103
1192
|
}
|
|
@@ -1108,8 +1197,10 @@ export class OxyServices {
|
|
|
1108
1197
|
*/
|
|
1109
1198
|
async getKarmaLeaderboard(): Promise<any> {
|
|
1110
1199
|
try {
|
|
1111
|
-
|
|
1112
|
-
|
|
1200
|
+
return await this.makeRequest('GET', '/api/karma/leaderboard', undefined, {
|
|
1201
|
+
cache: true,
|
|
1202
|
+
cacheTTL: 5 * 60 * 1000, // 5 minutes cache
|
|
1203
|
+
});
|
|
1113
1204
|
} catch (error) {
|
|
1114
1205
|
throw this.handleError(error);
|
|
1115
1206
|
}
|
|
@@ -1120,8 +1211,10 @@ export class OxyServices {
|
|
|
1120
1211
|
*/
|
|
1121
1212
|
async getKarmaRules(): Promise<any> {
|
|
1122
1213
|
try {
|
|
1123
|
-
|
|
1124
|
-
|
|
1214
|
+
return await this.makeRequest('GET', '/api/karma/rules', undefined, {
|
|
1215
|
+
cache: true,
|
|
1216
|
+
cacheTTL: 30 * 60 * 1000, // 30 minutes cache (rules don't change often)
|
|
1217
|
+
});
|
|
1125
1218
|
} catch (error) {
|
|
1126
1219
|
throw this.handleError(error);
|
|
1127
1220
|
}
|
|
@@ -1137,8 +1230,7 @@ export class OxyServices {
|
|
|
1137
1230
|
async deleteFile(fileId: string): Promise<any> {
|
|
1138
1231
|
try {
|
|
1139
1232
|
// Central Asset Service delete with force=true behavior controlled by caller via assetDelete
|
|
1140
|
-
|
|
1141
|
-
return res.data;
|
|
1233
|
+
return await this.makeRequest('DELETE', `/api/assets/${encodeURIComponent(fileId)}`, undefined, { cache: false });
|
|
1142
1234
|
} catch (error) {
|
|
1143
1235
|
throw this.handleError(error);
|
|
1144
1236
|
}
|
|
@@ -1153,7 +1245,7 @@ export class OxyServices {
|
|
|
1153
1245
|
if (variant) params.set('variant', variant);
|
|
1154
1246
|
if (expiresIn) params.set('expiresIn', String(expiresIn));
|
|
1155
1247
|
params.set('fallback', 'placeholderVisible');
|
|
1156
|
-
const token = this.
|
|
1248
|
+
const token = this.httpClient.getAccessToken();
|
|
1157
1249
|
if (token) params.set('token', token);
|
|
1158
1250
|
|
|
1159
1251
|
// Use params.toString() to detect whether there are query params.
|
|
@@ -1179,12 +1271,12 @@ export class OxyServices {
|
|
|
1179
1271
|
*/
|
|
1180
1272
|
async listUserFiles(limit?: number, offset?: number): Promise<{ files: any[]; total: number; hasMore: boolean }> {
|
|
1181
1273
|
try {
|
|
1182
|
-
const
|
|
1183
|
-
if (limit)
|
|
1184
|
-
if (offset)
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1274
|
+
const paramsObj: any = {};
|
|
1275
|
+
if (limit) paramsObj.limit = limit;
|
|
1276
|
+
if (offset) paramsObj.offset = offset;
|
|
1277
|
+
return await this.makeRequest('GET', '/api/assets', paramsObj, {
|
|
1278
|
+
cache: false, // Don't cache file lists - always get fresh data
|
|
1279
|
+
});
|
|
1188
1280
|
} catch (error) {
|
|
1189
1281
|
throw this.handleError(error);
|
|
1190
1282
|
}
|
|
@@ -1197,8 +1289,12 @@ export class OxyServices {
|
|
|
1197
1289
|
*/
|
|
1198
1290
|
async getFileContentAsText(fileId: string, variant?: string): Promise<string> {
|
|
1199
1291
|
try {
|
|
1200
|
-
const
|
|
1201
|
-
const
|
|
1292
|
+
const params: any = variant ? { variant } : undefined;
|
|
1293
|
+
const urlRes = await this.makeRequest<{ url: string }>('GET', `/api/assets/${encodeURIComponent(fileId)}/url`, params, {
|
|
1294
|
+
cache: true,
|
|
1295
|
+
cacheTTL: 10 * 60 * 1000, // 10 minutes cache for URLs
|
|
1296
|
+
});
|
|
1297
|
+
const downloadUrl = urlRes?.url;
|
|
1202
1298
|
const response = await fetch(downloadUrl);
|
|
1203
1299
|
return await response.text();
|
|
1204
1300
|
} catch (error) {
|
|
@@ -1211,8 +1307,12 @@ export class OxyServices {
|
|
|
1211
1307
|
*/
|
|
1212
1308
|
async getFileContentAsBlob(fileId: string, variant?: string): Promise<Blob> {
|
|
1213
1309
|
try {
|
|
1214
|
-
const
|
|
1215
|
-
const
|
|
1310
|
+
const params: any = variant ? { variant } : undefined;
|
|
1311
|
+
const urlRes = await this.makeRequest<{ url: string }>('GET', `/api/assets/${encodeURIComponent(fileId)}/url`, params, {
|
|
1312
|
+
cache: true,
|
|
1313
|
+
cacheTTL: 10 * 60 * 1000, // 10 minutes cache for URLs
|
|
1314
|
+
});
|
|
1315
|
+
const downloadUrl = urlRes?.url;
|
|
1216
1316
|
const response = await fetch(downloadUrl);
|
|
1217
1317
|
return await response.blob();
|
|
1218
1318
|
} catch (error) {
|
|
@@ -1247,12 +1347,11 @@ export class OxyServices {
|
|
|
1247
1347
|
*/
|
|
1248
1348
|
async assetInit(sha256: string, size: number, mime: string): Promise<AssetInitResponse> {
|
|
1249
1349
|
try {
|
|
1250
|
-
|
|
1350
|
+
return await this.makeRequest<AssetInitResponse>('POST', '/api/assets/init', {
|
|
1251
1351
|
sha256,
|
|
1252
1352
|
size,
|
|
1253
1353
|
mime
|
|
1254
|
-
});
|
|
1255
|
-
return res.data;
|
|
1354
|
+
}, { cache: false });
|
|
1256
1355
|
} catch (error) {
|
|
1257
1356
|
throw this.handleError(error);
|
|
1258
1357
|
}
|
|
@@ -1263,15 +1362,14 @@ export class OxyServices {
|
|
|
1263
1362
|
*/
|
|
1264
1363
|
async assetComplete(fileId: string, originalName: string, size: number, mime: string, visibility?: 'private' | 'public' | 'unlisted', metadata?: Record<string, any>): Promise<any> {
|
|
1265
1364
|
try {
|
|
1266
|
-
|
|
1365
|
+
return await this.makeRequest('POST', '/api/assets/complete', {
|
|
1267
1366
|
fileId,
|
|
1268
1367
|
originalName,
|
|
1269
1368
|
size,
|
|
1270
1369
|
mime,
|
|
1271
1370
|
visibility,
|
|
1272
1371
|
metadata
|
|
1273
|
-
});
|
|
1274
|
-
return res.data;
|
|
1372
|
+
}, { cache: false });
|
|
1275
1373
|
} catch (error) {
|
|
1276
1374
|
throw this.handleError(error);
|
|
1277
1375
|
}
|
|
@@ -1295,8 +1393,11 @@ export class OxyServices {
|
|
|
1295
1393
|
// Fallback: direct upload via API to avoid CORS issues
|
|
1296
1394
|
const fd = new FormData();
|
|
1297
1395
|
fd.append('file', file);
|
|
1298
|
-
|
|
1299
|
-
|
|
1396
|
+
// Use httpClient directly for FormData uploads (bypasses RequestManager for special handling)
|
|
1397
|
+
await this.httpClient.request({
|
|
1398
|
+
method: 'POST',
|
|
1399
|
+
url: `/api/assets/${encodeURIComponent(initResponse.fileId)}/upload-direct`,
|
|
1400
|
+
data: fd,
|
|
1300
1401
|
});
|
|
1301
1402
|
}
|
|
1302
1403
|
|
|
@@ -1354,8 +1455,7 @@ export class OxyServices {
|
|
|
1354
1455
|
const body: any = { app, entityType, entityId };
|
|
1355
1456
|
if (visibility) body.visibility = visibility;
|
|
1356
1457
|
if (webhookUrl) body.webhookUrl = webhookUrl;
|
|
1357
|
-
|
|
1358
|
-
return res.data;
|
|
1458
|
+
return await this.makeRequest('POST', `/api/assets/${fileId}/links`, body, { cache: false });
|
|
1359
1459
|
} catch (error) {
|
|
1360
1460
|
throw this.handleError(error);
|
|
1361
1461
|
}
|
|
@@ -1366,14 +1466,11 @@ export class OxyServices {
|
|
|
1366
1466
|
*/
|
|
1367
1467
|
async assetUnlink(fileId: string, app: string, entityType: string, entityId: string): Promise<any> {
|
|
1368
1468
|
try {
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
}
|
|
1375
|
-
});
|
|
1376
|
-
return res.data;
|
|
1469
|
+
return await this.makeRequest('DELETE', `/api/assets/${fileId}/links`, {
|
|
1470
|
+
app,
|
|
1471
|
+
entityType,
|
|
1472
|
+
entityId
|
|
1473
|
+
}, { cache: false });
|
|
1377
1474
|
} catch (error) {
|
|
1378
1475
|
throw this.handleError(error);
|
|
1379
1476
|
}
|
|
@@ -1384,8 +1481,10 @@ export class OxyServices {
|
|
|
1384
1481
|
*/
|
|
1385
1482
|
async assetGet(fileId: string): Promise<any> {
|
|
1386
1483
|
try {
|
|
1387
|
-
|
|
1388
|
-
|
|
1484
|
+
return await this.makeRequest('GET', `/api/assets/${fileId}`, undefined, {
|
|
1485
|
+
cache: true,
|
|
1486
|
+
cacheTTL: 5 * 60 * 1000, // 5 minutes cache
|
|
1487
|
+
});
|
|
1389
1488
|
} catch (error) {
|
|
1390
1489
|
throw this.handleError(error);
|
|
1391
1490
|
}
|
|
@@ -1396,15 +1495,14 @@ export class OxyServices {
|
|
|
1396
1495
|
*/
|
|
1397
1496
|
async assetGetUrl(fileId: string, variant?: string, expiresIn?: number): Promise<AssetUrlResponse> {
|
|
1398
1497
|
try {
|
|
1399
|
-
const params =
|
|
1400
|
-
if (variant) params.
|
|
1401
|
-
if (expiresIn) params.
|
|
1402
|
-
|
|
1403
|
-
const queryString = params.toString();
|
|
1404
|
-
const url = `/api/assets/${fileId}/url${queryString ? `?${queryString}` : ''}`;
|
|
1498
|
+
const params: any = {};
|
|
1499
|
+
if (variant) params.variant = variant;
|
|
1500
|
+
if (expiresIn) params.expiresIn = expiresIn;
|
|
1405
1501
|
|
|
1406
|
-
|
|
1407
|
-
|
|
1502
|
+
return await this.makeRequest<AssetUrlResponse>('GET', `/api/assets/${fileId}/url`, params, {
|
|
1503
|
+
cache: true,
|
|
1504
|
+
cacheTTL: 10 * 60 * 1000, // 10 minutes cache for URLs
|
|
1505
|
+
});
|
|
1408
1506
|
} catch (error) {
|
|
1409
1507
|
throw this.handleError(error);
|
|
1410
1508
|
}
|
|
@@ -1415,8 +1513,7 @@ export class OxyServices {
|
|
|
1415
1513
|
*/
|
|
1416
1514
|
async assetRestore(fileId: string): Promise<any> {
|
|
1417
1515
|
try {
|
|
1418
|
-
|
|
1419
|
-
return res.data;
|
|
1516
|
+
return await this.makeRequest('POST', `/api/assets/${fileId}/restore`, undefined, { cache: false });
|
|
1420
1517
|
} catch (error) {
|
|
1421
1518
|
throw this.handleError(error);
|
|
1422
1519
|
}
|
|
@@ -1427,9 +1524,8 @@ export class OxyServices {
|
|
|
1427
1524
|
*/
|
|
1428
1525
|
async assetDelete(fileId: string, force: boolean = false): Promise<any> {
|
|
1429
1526
|
try {
|
|
1430
|
-
const params = force ? '
|
|
1431
|
-
|
|
1432
|
-
return res.data;
|
|
1527
|
+
const params: any = force ? { force: 'true' } : undefined;
|
|
1528
|
+
return await this.makeRequest('DELETE', `/api/assets/${fileId}`, params, { cache: false });
|
|
1433
1529
|
} catch (error) {
|
|
1434
1530
|
throw this.handleError(error);
|
|
1435
1531
|
}
|
|
@@ -1455,10 +1551,9 @@ export class OxyServices {
|
|
|
1455
1551
|
*/
|
|
1456
1552
|
async assetUpdateVisibility(fileId: string, visibility: 'private' | 'public' | 'unlisted'): Promise<any> {
|
|
1457
1553
|
try {
|
|
1458
|
-
|
|
1554
|
+
return await this.makeRequest('PATCH', `/api/assets/${fileId}/visibility`, {
|
|
1459
1555
|
visibility
|
|
1460
|
-
});
|
|
1461
|
-
return res.data;
|
|
1556
|
+
}, { cache: false });
|
|
1462
1557
|
} catch (error) {
|
|
1463
1558
|
throw this.handleError(error);
|
|
1464
1559
|
}
|
|
@@ -1515,8 +1610,11 @@ export class OxyServices {
|
|
|
1515
1610
|
*/
|
|
1516
1611
|
async getDeveloperApps(): Promise<any[]> {
|
|
1517
1612
|
try {
|
|
1518
|
-
const res = await this.
|
|
1519
|
-
|
|
1613
|
+
const res = await this.makeRequest<{ apps?: any[] }>('GET', '/api/developer/apps', undefined, {
|
|
1614
|
+
cache: true,
|
|
1615
|
+
cacheTTL: 2 * 60 * 1000, // 2 minutes cache
|
|
1616
|
+
});
|
|
1617
|
+
return res.apps || [];
|
|
1520
1618
|
} catch (error) {
|
|
1521
1619
|
throw this.handleError(error);
|
|
1522
1620
|
}
|
|
@@ -1533,8 +1631,8 @@ export class OxyServices {
|
|
|
1533
1631
|
scopes?: string[];
|
|
1534
1632
|
}): Promise<any> {
|
|
1535
1633
|
try {
|
|
1536
|
-
const res = await this.
|
|
1537
|
-
return res.
|
|
1634
|
+
const res = await this.makeRequest<{ app: any }>('POST', '/api/developer/apps', data, { cache: false });
|
|
1635
|
+
return res.app;
|
|
1538
1636
|
} catch (error) {
|
|
1539
1637
|
throw this.handleError(error);
|
|
1540
1638
|
}
|
|
@@ -1545,8 +1643,11 @@ export class OxyServices {
|
|
|
1545
1643
|
*/
|
|
1546
1644
|
async getDeveloperApp(appId: string): Promise<any> {
|
|
1547
1645
|
try {
|
|
1548
|
-
const res = await this.
|
|
1549
|
-
|
|
1646
|
+
const res = await this.makeRequest<{ app: any }>('GET', `/api/developer/apps/${appId}`, undefined, {
|
|
1647
|
+
cache: true,
|
|
1648
|
+
cacheTTL: 5 * 60 * 1000, // 5 minutes cache
|
|
1649
|
+
});
|
|
1650
|
+
return res.app;
|
|
1550
1651
|
} catch (error) {
|
|
1551
1652
|
throw this.handleError(error);
|
|
1552
1653
|
}
|
|
@@ -1563,8 +1664,8 @@ export class OxyServices {
|
|
|
1563
1664
|
scopes?: string[];
|
|
1564
1665
|
}): Promise<any> {
|
|
1565
1666
|
try {
|
|
1566
|
-
const res = await this.
|
|
1567
|
-
return res.
|
|
1667
|
+
const res = await this.makeRequest<{ app: any }>('PATCH', `/api/developer/apps/${appId}`, data, { cache: false });
|
|
1668
|
+
return res.app;
|
|
1568
1669
|
} catch (error) {
|
|
1569
1670
|
throw this.handleError(error);
|
|
1570
1671
|
}
|
|
@@ -1575,8 +1676,7 @@ export class OxyServices {
|
|
|
1575
1676
|
*/
|
|
1576
1677
|
async regenerateDeveloperAppSecret(appId: string): Promise<any> {
|
|
1577
1678
|
try {
|
|
1578
|
-
|
|
1579
|
-
return res.data;
|
|
1679
|
+
return await this.makeRequest('POST', `/api/developer/apps/${appId}/regenerate-secret`, undefined, { cache: false });
|
|
1580
1680
|
} catch (error) {
|
|
1581
1681
|
throw this.handleError(error);
|
|
1582
1682
|
}
|
|
@@ -1587,8 +1687,7 @@ export class OxyServices {
|
|
|
1587
1687
|
*/
|
|
1588
1688
|
async deleteDeveloperApp(appId: string): Promise<any> {
|
|
1589
1689
|
try {
|
|
1590
|
-
|
|
1591
|
-
return res.data;
|
|
1690
|
+
return await this.makeRequest('DELETE', `/api/developer/apps/${appId}`, undefined, { cache: false });
|
|
1592
1691
|
} catch (error) {
|
|
1593
1692
|
throw this.handleError(error);
|
|
1594
1693
|
}
|
|
@@ -1603,11 +1702,10 @@ export class OxyServices {
|
|
|
1603
1702
|
*/
|
|
1604
1703
|
async updateLocation(latitude: number, longitude: number): Promise<any> {
|
|
1605
1704
|
try {
|
|
1606
|
-
|
|
1705
|
+
return await this.makeRequest('POST', '/api/location', {
|
|
1607
1706
|
latitude,
|
|
1608
1707
|
longitude
|
|
1609
|
-
});
|
|
1610
|
-
return res.data;
|
|
1708
|
+
}, { cache: false });
|
|
1611
1709
|
} catch (error) {
|
|
1612
1710
|
throw this.handleError(error);
|
|
1613
1711
|
}
|
|
@@ -1618,9 +1716,10 @@ export class OxyServices {
|
|
|
1618
1716
|
*/
|
|
1619
1717
|
async getNearbyUsers(radius?: number): Promise<any[]> {
|
|
1620
1718
|
try {
|
|
1621
|
-
const params = radius ?
|
|
1622
|
-
|
|
1623
|
-
|
|
1719
|
+
const params: any = radius ? { radius } : undefined;
|
|
1720
|
+
return await this.makeRequest('GET', '/api/location/nearby', params, {
|
|
1721
|
+
cache: false, // Don't cache location data - always get fresh data
|
|
1722
|
+
});
|
|
1624
1723
|
} catch (error) {
|
|
1625
1724
|
throw this.handleError(error);
|
|
1626
1725
|
}
|
|
@@ -1635,10 +1734,10 @@ export class OxyServices {
|
|
|
1635
1734
|
*/
|
|
1636
1735
|
async trackEvent(eventName: string, properties?: Record<string, any>): Promise<void> {
|
|
1637
1736
|
try {
|
|
1638
|
-
await this.
|
|
1737
|
+
await this.makeRequest('POST', '/api/analytics/events', {
|
|
1639
1738
|
event: eventName,
|
|
1640
1739
|
properties
|
|
1641
|
-
});
|
|
1740
|
+
}, { cache: false, retry: false }); // Don't retry analytics events
|
|
1642
1741
|
} catch (error) {
|
|
1643
1742
|
throw this.handleError(error);
|
|
1644
1743
|
}
|
|
@@ -1649,12 +1748,14 @@ export class OxyServices {
|
|
|
1649
1748
|
*/
|
|
1650
1749
|
async getAnalytics(startDate?: string, endDate?: string): Promise<any> {
|
|
1651
1750
|
try {
|
|
1652
|
-
const params =
|
|
1653
|
-
if (startDate) params.
|
|
1654
|
-
if (endDate) params.
|
|
1751
|
+
const params: any = {};
|
|
1752
|
+
if (startDate) params.startDate = startDate;
|
|
1753
|
+
if (endDate) params.endDate = endDate;
|
|
1655
1754
|
|
|
1656
|
-
|
|
1657
|
-
|
|
1755
|
+
return await this.makeRequest('GET', '/api/analytics', params, {
|
|
1756
|
+
cache: true,
|
|
1757
|
+
cacheTTL: 5 * 60 * 1000, // 5 minutes cache
|
|
1758
|
+
});
|
|
1658
1759
|
} catch (error) {
|
|
1659
1760
|
throw this.handleError(error);
|
|
1660
1761
|
}
|
|
@@ -1669,8 +1770,7 @@ export class OxyServices {
|
|
|
1669
1770
|
*/
|
|
1670
1771
|
async registerDevice(deviceData: any): Promise<any> {
|
|
1671
1772
|
try {
|
|
1672
|
-
|
|
1673
|
-
return res.data;
|
|
1773
|
+
return await this.makeRequest('POST', '/api/devices', deviceData, { cache: false });
|
|
1674
1774
|
} catch (error) {
|
|
1675
1775
|
throw this.handleError(error);
|
|
1676
1776
|
}
|
|
@@ -1681,8 +1781,9 @@ export class OxyServices {
|
|
|
1681
1781
|
*/
|
|
1682
1782
|
async getUserDevices(): Promise<any[]> {
|
|
1683
1783
|
try {
|
|
1684
|
-
|
|
1685
|
-
|
|
1784
|
+
return await this.makeRequest('GET', '/api/devices', undefined, {
|
|
1785
|
+
cache: false, // Don't cache device list - always get fresh data
|
|
1786
|
+
});
|
|
1686
1787
|
} catch (error) {
|
|
1687
1788
|
throw this.handleError(error);
|
|
1688
1789
|
}
|
|
@@ -1693,7 +1794,7 @@ export class OxyServices {
|
|
|
1693
1794
|
*/
|
|
1694
1795
|
async removeDevice(deviceId: string): Promise<void> {
|
|
1695
1796
|
try {
|
|
1696
|
-
await this.
|
|
1797
|
+
await this.makeRequest('DELETE', `/api/devices/${deviceId}`, undefined, { cache: false });
|
|
1697
1798
|
} catch (error) {
|
|
1698
1799
|
throw this.handleError(error);
|
|
1699
1800
|
}
|
|
@@ -1701,11 +1802,16 @@ export class OxyServices {
|
|
|
1701
1802
|
|
|
1702
1803
|
/**
|
|
1703
1804
|
* Get device sessions
|
|
1805
|
+
* Note: Not cached by default to ensure fresh data, but can be cached via makeRequest if needed
|
|
1704
1806
|
*/
|
|
1705
1807
|
async getDeviceSessions(sessionId: string): Promise<any[]> {
|
|
1706
1808
|
try {
|
|
1707
|
-
|
|
1708
|
-
|
|
1809
|
+
// Use makeRequest for consistent error handling and optional caching
|
|
1810
|
+
// Cache disabled by default to ensure fresh session data
|
|
1811
|
+
return await this.makeRequest<any[]>('GET', `/api/session/device/sessions/${sessionId}`, undefined, {
|
|
1812
|
+
cache: false, // Don't cache sessions - always get fresh data
|
|
1813
|
+
deduplicate: true, // Deduplicate concurrent requests for same sessionId
|
|
1814
|
+
});
|
|
1709
1815
|
} catch (error) {
|
|
1710
1816
|
throw this.handleError(error);
|
|
1711
1817
|
}
|
|
@@ -1720,8 +1826,11 @@ export class OxyServices {
|
|
|
1720
1826
|
if (deviceId) params.append('deviceId', deviceId);
|
|
1721
1827
|
if (excludeCurrent) params.append('excludeCurrent', 'true');
|
|
1722
1828
|
|
|
1723
|
-
const
|
|
1724
|
-
|
|
1829
|
+
const urlParams: any = {};
|
|
1830
|
+
params.forEach((value, key) => {
|
|
1831
|
+
urlParams[key] = value;
|
|
1832
|
+
});
|
|
1833
|
+
return await this.makeRequest('POST', `/api/session/device/logout-all/${sessionId}`, urlParams, { cache: false });
|
|
1725
1834
|
} catch (error) {
|
|
1726
1835
|
throw this.handleError(error);
|
|
1727
1836
|
}
|
|
@@ -1732,8 +1841,7 @@ export class OxyServices {
|
|
|
1732
1841
|
*/
|
|
1733
1842
|
async updateDeviceName(sessionId: string, deviceName: string): Promise<any> {
|
|
1734
1843
|
try {
|
|
1735
|
-
|
|
1736
|
-
return res.data;
|
|
1844
|
+
return await this.makeRequest('PUT', `/api/session/device/name/${sessionId}`, { deviceName }, { cache: false });
|
|
1737
1845
|
} catch (error) {
|
|
1738
1846
|
throw this.handleError(error);
|
|
1739
1847
|
}
|
|
@@ -1753,8 +1861,15 @@ export class OxyServices {
|
|
|
1753
1861
|
image?: string;
|
|
1754
1862
|
}> {
|
|
1755
1863
|
try {
|
|
1756
|
-
|
|
1757
|
-
|
|
1864
|
+
return await this.makeRequest<{
|
|
1865
|
+
url: string;
|
|
1866
|
+
title: string;
|
|
1867
|
+
description: string;
|
|
1868
|
+
image?: string;
|
|
1869
|
+
}>('GET', '/api/link-metadata', { url }, {
|
|
1870
|
+
cache: true,
|
|
1871
|
+
cacheTTL: 30 * 60 * 1000, // 30 minutes cache for link metadata
|
|
1872
|
+
});
|
|
1758
1873
|
} catch (error) {
|
|
1759
1874
|
throw this.handleError(error);
|
|
1760
1875
|
}
|