@oxyhq/services 5.9.2 → 5.9.4

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.
Files changed (202) hide show
  1. package/README.md +1 -33
  2. package/lib/commonjs/core/OxyServices.js +322 -0
  3. package/lib/commonjs/core/OxyServices.js.map +1 -0
  4. package/lib/commonjs/core/OxyServicesMain.js +51 -0
  5. package/lib/commonjs/core/OxyServicesMain.js.map +1 -0
  6. package/lib/commonjs/core/analytics/AnalyticsService.js +67 -0
  7. package/lib/commonjs/core/analytics/AnalyticsService.js.map +1 -0
  8. package/lib/commonjs/core/auth/AuthService.js +526 -0
  9. package/lib/commonjs/core/auth/AuthService.js.map +1 -0
  10. package/lib/commonjs/core/devices/DeviceService.js +61 -0
  11. package/lib/commonjs/core/devices/DeviceService.js.map +1 -0
  12. package/lib/commonjs/core/files/FileService.js +176 -0
  13. package/lib/commonjs/core/files/FileService.js.map +1 -0
  14. package/lib/commonjs/core/index.js +103 -1701
  15. package/lib/commonjs/core/index.js.map +1 -1
  16. package/lib/commonjs/core/karma/KarmaService.js +100 -0
  17. package/lib/commonjs/core/karma/KarmaService.js.map +1 -0
  18. package/lib/commonjs/core/locations/LocationService.js +131 -0
  19. package/lib/commonjs/core/locations/LocationService.js.map +1 -0
  20. package/lib/commonjs/core/payments/PaymentService.js +124 -0
  21. package/lib/commonjs/core/payments/PaymentService.js.map +1 -0
  22. package/lib/commonjs/core/users/UserService.js +234 -0
  23. package/lib/commonjs/core/users/UserService.js.map +1 -0
  24. package/lib/commonjs/index.js +164 -3
  25. package/lib/commonjs/index.js.map +1 -1
  26. package/lib/commonjs/models/session.js +2 -0
  27. package/lib/commonjs/{types/middleware.js.map → models/session.js.map} +1 -1
  28. package/lib/commonjs/ui/context/OxyContext.js +28 -24
  29. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  30. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +2 -2
  31. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
  32. package/lib/commonjs/ui/screens/FileManagementScreen.js +12 -12
  33. package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
  34. package/lib/commonjs/ui/screens/ProfileScreen.js +2 -2
  35. package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
  36. package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
  37. package/lib/commonjs/ui/screens/SignInScreen.js +1 -1
  38. package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
  39. package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js +1 -1
  40. package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
  41. package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js +1 -1
  42. package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js.map +1 -1
  43. package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js +1 -1
  44. package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js.map +1 -1
  45. package/lib/commonjs/ui/stores/followStore.js +4 -4
  46. package/lib/commonjs/ui/stores/followStore.js.map +1 -1
  47. package/lib/commonjs/utils/apiUtils.js +93 -0
  48. package/lib/commonjs/utils/apiUtils.js.map +1 -0
  49. package/lib/commonjs/utils/asyncUtils.js +219 -0
  50. package/lib/commonjs/utils/asyncUtils.js.map +1 -0
  51. package/lib/commonjs/utils/errorUtils.js +148 -0
  52. package/lib/commonjs/utils/errorUtils.js.map +1 -0
  53. package/lib/commonjs/utils/hookUtils.js +399 -0
  54. package/lib/commonjs/utils/hookUtils.js.map +1 -0
  55. package/lib/commonjs/utils/loggerUtils.js +160 -0
  56. package/lib/commonjs/utils/loggerUtils.js.map +1 -0
  57. package/lib/commonjs/utils/validationUtils.js +174 -0
  58. package/lib/commonjs/utils/validationUtils.js.map +1 -0
  59. package/lib/module/core/OxyServices.js +316 -0
  60. package/lib/module/core/OxyServices.js.map +1 -0
  61. package/lib/module/core/OxyServicesMain.js +47 -0
  62. package/lib/module/core/OxyServicesMain.js.map +1 -0
  63. package/lib/module/core/analytics/AnalyticsService.js +62 -0
  64. package/lib/module/core/analytics/AnalyticsService.js.map +1 -0
  65. package/lib/module/core/auth/AuthService.js +521 -0
  66. package/lib/module/core/auth/AuthService.js.map +1 -0
  67. package/lib/module/core/devices/DeviceService.js +57 -0
  68. package/lib/module/core/devices/DeviceService.js.map +1 -0
  69. package/lib/module/core/files/FileService.js +171 -0
  70. package/lib/module/core/files/FileService.js.map +1 -0
  71. package/lib/module/core/index.js +25 -1690
  72. package/lib/module/core/index.js.map +1 -1
  73. package/lib/module/core/karma/KarmaService.js +95 -0
  74. package/lib/module/core/karma/KarmaService.js.map +1 -0
  75. package/lib/module/core/locations/LocationService.js +127 -0
  76. package/lib/module/core/locations/LocationService.js.map +1 -0
  77. package/lib/module/core/payments/PaymentService.js +119 -0
  78. package/lib/module/core/payments/PaymentService.js.map +1 -0
  79. package/lib/module/core/users/UserService.js +230 -0
  80. package/lib/module/core/users/UserService.js.map +1 -0
  81. package/lib/module/index.js +8 -4
  82. package/lib/module/index.js.map +1 -1
  83. package/lib/module/models/session.js +2 -0
  84. package/lib/module/{types/middleware.js.map → models/session.js.map} +1 -1
  85. package/lib/module/ui/context/OxyContext.js +28 -24
  86. package/lib/module/ui/context/OxyContext.js.map +1 -1
  87. package/lib/module/ui/screens/AccountSwitcherScreen.js +2 -2
  88. package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
  89. package/lib/module/ui/screens/FileManagementScreen.js +12 -12
  90. package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
  91. package/lib/module/ui/screens/ProfileScreen.js +2 -2
  92. package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
  93. package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
  94. package/lib/module/ui/screens/SignInScreen.js +1 -1
  95. package/lib/module/ui/screens/SignInScreen.js.map +1 -1
  96. package/lib/module/ui/screens/karma/KarmaCenterScreen.js +1 -1
  97. package/lib/module/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
  98. package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js +1 -1
  99. package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js.map +1 -1
  100. package/lib/module/ui/screens/karma/KarmaRulesScreen.js +1 -1
  101. package/lib/module/ui/screens/karma/KarmaRulesScreen.js.map +1 -1
  102. package/lib/module/ui/stores/followStore.js +4 -4
  103. package/lib/module/ui/stores/followStore.js.map +1 -1
  104. package/lib/module/utils/apiUtils.js +85 -0
  105. package/lib/module/utils/apiUtils.js.map +1 -0
  106. package/lib/module/utils/asyncUtils.js +202 -0
  107. package/lib/module/utils/asyncUtils.js.map +1 -0
  108. package/lib/module/utils/errorUtils.js +139 -0
  109. package/lib/module/utils/errorUtils.js.map +1 -0
  110. package/lib/module/utils/hookUtils.js +381 -0
  111. package/lib/module/utils/hookUtils.js.map +1 -0
  112. package/lib/module/utils/loggerUtils.js +149 -0
  113. package/lib/module/utils/loggerUtils.js.map +1 -0
  114. package/lib/module/utils/validationUtils.js +154 -0
  115. package/lib/module/utils/validationUtils.js.map +1 -0
  116. package/lib/typescript/core/OxyServices.d.ts +99 -0
  117. package/lib/typescript/core/OxyServices.d.ts.map +1 -0
  118. package/lib/typescript/core/OxyServicesMain.d.ts +33 -0
  119. package/lib/typescript/core/OxyServicesMain.d.ts.map +1 -0
  120. package/lib/typescript/core/analytics/AnalyticsService.d.ts +26 -0
  121. package/lib/typescript/core/analytics/AnalyticsService.d.ts.map +1 -0
  122. package/lib/typescript/core/auth/AuthService.d.ts +165 -0
  123. package/lib/typescript/core/auth/AuthService.d.ts.map +1 -0
  124. package/lib/typescript/core/devices/DeviceService.d.ts +20 -0
  125. package/lib/typescript/core/devices/DeviceService.d.ts.map +1 -0
  126. package/lib/typescript/core/files/FileService.d.ts +59 -0
  127. package/lib/typescript/core/files/FileService.d.ts.map +1 -0
  128. package/lib/typescript/core/index.d.ts +19 -656
  129. package/lib/typescript/core/index.d.ts.map +1 -1
  130. package/lib/typescript/core/karma/KarmaService.d.ts +50 -0
  131. package/lib/typescript/core/karma/KarmaService.d.ts.map +1 -0
  132. package/lib/typescript/core/locations/LocationService.d.ts +39 -0
  133. package/lib/typescript/core/locations/LocationService.d.ts.map +1 -0
  134. package/lib/typescript/core/payments/PaymentService.d.ts +50 -0
  135. package/lib/typescript/core/payments/PaymentService.d.ts.map +1 -0
  136. package/lib/typescript/core/users/UserService.d.ts +111 -0
  137. package/lib/typescript/core/users/UserService.d.ts.map +1 -0
  138. package/lib/typescript/index.d.ts +7 -3
  139. package/lib/typescript/index.d.ts.map +1 -1
  140. package/lib/typescript/models/{secureSession.d.ts → session.d.ts} +4 -4
  141. package/lib/typescript/models/session.d.ts.map +1 -0
  142. package/lib/typescript/ui/context/OxyContext.d.ts +2 -2
  143. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  144. package/lib/typescript/utils/apiUtils.d.ts +61 -0
  145. package/lib/typescript/utils/apiUtils.d.ts.map +1 -0
  146. package/lib/typescript/utils/asyncUtils.d.ts +64 -0
  147. package/lib/typescript/utils/asyncUtils.d.ts.map +1 -0
  148. package/lib/typescript/utils/errorUtils.d.ts +45 -0
  149. package/lib/typescript/utils/errorUtils.d.ts.map +1 -0
  150. package/lib/typescript/utils/hookUtils.d.ts +102 -0
  151. package/lib/typescript/utils/hookUtils.d.ts.map +1 -0
  152. package/lib/typescript/utils/loggerUtils.d.ts +49 -0
  153. package/lib/typescript/utils/loggerUtils.d.ts.map +1 -0
  154. package/lib/typescript/utils/validationUtils.d.ts +80 -0
  155. package/lib/typescript/utils/validationUtils.d.ts.map +1 -0
  156. package/package.json +2 -8
  157. package/src/core/OxyServices.ts +351 -0
  158. package/src/core/OxyServicesMain.ts +57 -0
  159. package/src/core/analytics/AnalyticsService.ts +64 -0
  160. package/src/core/auth/AuthService.ts +544 -0
  161. package/src/core/devices/DeviceService.ts +55 -0
  162. package/src/core/files/FileService.ts +194 -0
  163. package/src/core/index.ts +27 -1765
  164. package/src/core/karma/KarmaService.ts +104 -0
  165. package/src/core/locations/LocationService.ts +141 -0
  166. package/src/core/payments/PaymentService.ts +133 -0
  167. package/src/core/users/UserService.ts +241 -0
  168. package/src/index.ts +29 -8
  169. package/src/models/{secureSession.ts → session.ts} +5 -5
  170. package/src/ui/context/OxyContext.tsx +34 -30
  171. package/src/ui/screens/AccountSwitcherScreen.tsx +4 -4
  172. package/src/ui/screens/FileManagementScreen.tsx +12 -12
  173. package/src/ui/screens/ProfileScreen.tsx +3 -3
  174. package/src/ui/screens/SessionManagementScreen.tsx +2 -2
  175. package/src/ui/screens/SignInScreen.tsx +1 -1
  176. package/src/ui/screens/karma/KarmaCenterScreen.tsx +2 -2
  177. package/src/ui/screens/karma/KarmaLeaderboardScreen.tsx +3 -3
  178. package/src/ui/screens/karma/KarmaRulesScreen.tsx +3 -3
  179. package/src/ui/stores/followStore.ts +4 -4
  180. package/src/utils/apiUtils.ts +102 -0
  181. package/src/utils/asyncUtils.ts +265 -0
  182. package/src/utils/errorUtils.ts +172 -0
  183. package/src/utils/hookUtils.ts +397 -0
  184. package/src/utils/loggerUtils.ts +153 -0
  185. package/src/utils/validationUtils.ts +158 -0
  186. package/lib/commonjs/middleware.js +0 -17
  187. package/lib/commonjs/middleware.js.map +0 -1
  188. package/lib/commonjs/models/secureSession.js +0 -2
  189. package/lib/commonjs/models/secureSession.js.map +0 -1
  190. package/lib/commonjs/types/middleware.js +0 -6
  191. package/lib/module/middleware.js +0 -12
  192. package/lib/module/middleware.js.map +0 -1
  193. package/lib/module/models/secureSession.js +0 -2
  194. package/lib/module/models/secureSession.js.map +0 -1
  195. package/lib/module/types/middleware.js +0 -4
  196. package/lib/typescript/middleware.d.ts +0 -9
  197. package/lib/typescript/middleware.d.ts.map +0 -1
  198. package/lib/typescript/models/secureSession.d.ts.map +0 -1
  199. package/lib/typescript/types/middleware.d.ts +0 -19
  200. package/lib/typescript/types/middleware.d.ts.map +0 -1
  201. package/src/middleware.ts +0 -11
  202. package/src/types/middleware.ts +0 -20
@@ -0,0 +1,64 @@
1
+ import { OxyServices } from '../OxyServices';
2
+ import { AnalyticsData, FollowerDetails, ContentViewer } from '../../models/interfaces';
3
+
4
+ /**
5
+ * Analytics service for handling analytics and content viewer operations
6
+ */
7
+ export class AnalyticsService extends OxyServices {
8
+ /**
9
+ * Get analytics data for user
10
+ */
11
+ async getAnalytics(userId: string, period?: string): Promise<AnalyticsData> {
12
+ try {
13
+ const params = new URLSearchParams();
14
+ if (period) params.append('period', period);
15
+
16
+ const res = await this.getClient().get(`/analytics/users/${userId}?${params.toString()}`);
17
+ return res.data;
18
+ } catch (error) {
19
+ throw this.handleError(error);
20
+ }
21
+ }
22
+
23
+ /**
24
+ * Update analytics data
25
+ */
26
+ async updateAnalytics(userId: string, type: string, data: Record<string, any>): Promise<{ message: string }> {
27
+ try {
28
+ const res = await this.getClient().put(`/analytics/users/${userId}/${type}`, data);
29
+ return res.data;
30
+ } catch (error) {
31
+ throw this.handleError(error);
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Get content viewers for user
37
+ */
38
+ async getContentViewers(userId: string, period?: string): Promise<ContentViewer[]> {
39
+ try {
40
+ const params = new URLSearchParams();
41
+ if (period) params.append('period', period);
42
+
43
+ const res = await this.getClient().get(`/analytics/users/${userId}/viewers?${params.toString()}`);
44
+ return res.data;
45
+ } catch (error) {
46
+ throw this.handleError(error);
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Get follower details for user
52
+ */
53
+ async getFollowerDetails(userId: string, period?: string): Promise<FollowerDetails> {
54
+ try {
55
+ const params = new URLSearchParams();
56
+ if (period) params.append('period', period);
57
+
58
+ const res = await this.getClient().get(`/analytics/users/${userId}/followers?${params.toString()}`);
59
+ return res.data;
60
+ } catch (error) {
61
+ throw this.handleError(error);
62
+ }
63
+ }
64
+ }
@@ -0,0 +1,544 @@
1
+ import { jwtDecode } from 'jwt-decode';
2
+ import { OxyServices } from '../OxyServices';
3
+ import { User, LoginResponse, ApiError } from '../../models/interfaces';
4
+ import { SessionLoginResponse } from '../../models/session';
5
+ import { validateAndSanitizeUserInput } from '../../utils/validationUtils';
6
+ import { validateRequiredFields } from '../../utils/errorUtils';
7
+
8
+ interface JwtPayload {
9
+ exp: number;
10
+ userId: string;
11
+ [key: string]: any;
12
+ }
13
+
14
+ /**
15
+ * Authentication service for handling login, signup, and session management
16
+ */
17
+ export class AuthService extends OxyServices {
18
+ /**
19
+ * Sign up a new user
20
+ */
21
+ async signUp(username: string, email: string, password: string): Promise<{ message: string; token: string; user: User }> {
22
+ try {
23
+ const res = await this.getClient().post('/auth/signup', {
24
+ username,
25
+ email,
26
+ password
27
+ });
28
+ return res.data;
29
+ } catch (error) {
30
+ throw this.handleError(error);
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Sign in with device management
36
+ */
37
+ async signIn(username: string, password: string, deviceName?: string, deviceFingerprint?: any): Promise<SessionLoginResponse> {
38
+ try {
39
+ const res = await this.getClient().post('/auth/login', {
40
+ username,
41
+ password,
42
+ deviceName,
43
+ deviceFingerprint
44
+ });
45
+ return res.data;
46
+ } catch (error) {
47
+ throw this.handleError(error);
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Get user by session ID
53
+ */
54
+ async getUserBySession(sessionId: string): Promise<User> {
55
+ try {
56
+ const res = await this.getClient().get(`/session/user/${sessionId}`);
57
+ return res.data;
58
+ } catch (error) {
59
+ throw this.handleError(error);
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Get access token by session ID
65
+ */
66
+ async getTokenBySession(sessionId: string): Promise<{ accessToken: string; expiresAt: string }> {
67
+ try {
68
+ const res = await this.getClient().get(`/session/token/${sessionId}`);
69
+ return res.data;
70
+ } catch (error) {
71
+ throw this.handleError(error);
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Get sessions by session ID
77
+ */
78
+ async getSessionsBySessionId(sessionId: string): Promise<any[]> {
79
+ try {
80
+ const res = await this.getClient().get(`/session/sessions/${sessionId}`);
81
+ return res.data;
82
+ } catch (error) {
83
+ throw this.handleError(error);
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Logout from a specific session
89
+ */
90
+ async logoutSession(sessionId: string, targetSessionId?: string): Promise<void> {
91
+ try {
92
+ await this.getClient().delete(`/session/logout/${sessionId}`, {
93
+ data: { targetSessionId }
94
+ });
95
+ } catch (error) {
96
+ throw this.handleError(error);
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Logout from all sessions
102
+ */
103
+ async logoutAllSessions(sessionId: string): Promise<void> {
104
+ try {
105
+ await this.getClient().delete(`/session/logout-all/${sessionId}`);
106
+ } catch (error) {
107
+ throw this.handleError(error);
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Validate a session with optional device fingerprint
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * // Basic session validation
117
+ * const result = await authService.validateSession('session-id');
118
+ *
119
+ * // With device fingerprint for enhanced security
120
+ * const result = await authService.validateSession('session-id', {
121
+ * deviceFingerprint: 'device-fingerprint',
122
+ * useHeaderValidation: true
123
+ * });
124
+ * ```
125
+ *
126
+ * @param sessionId The session ID to validate
127
+ * @param options Validation options
128
+ * @param options.deviceFingerprint Optional device fingerprint for enhanced validation
129
+ * @param options.useHeaderValidation Whether to use header-based validation (default: false)
130
+ * @returns Session validation result
131
+ */
132
+ async validateSession(
133
+ sessionId: string,
134
+ options: {
135
+ deviceFingerprint?: string;
136
+ useHeaderValidation?: boolean;
137
+ } = {}
138
+ ): Promise<{
139
+ valid: boolean;
140
+ expiresAt: string;
141
+ lastActivity: string;
142
+ user: User;
143
+ sessionId?: string;
144
+ source?: string;
145
+ }> {
146
+ const { deviceFingerprint, useHeaderValidation = false } = options;
147
+
148
+ try {
149
+ if (useHeaderValidation) {
150
+ // Use header-based validation with device fingerprint
151
+ const res = await this.getClient().get(`/session/validate-header/${sessionId}`, {
152
+ headers: deviceFingerprint ? { 'X-Device-Fingerprint': deviceFingerprint } : {}
153
+ });
154
+ return { ...res.data, source: 'header' };
155
+ } else {
156
+ // Use standard session validation
157
+ const res = await this.getClient().get(`/session/validate/${sessionId}`);
158
+ return { ...res.data, source: 'standard' };
159
+ }
160
+ } catch (error) {
161
+ throw this.handleError(error);
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Express.js authentication middleware
167
+ *
168
+ * A simple, efficient, and debuggable authentication middleware that supports both
169
+ * session-based and legacy token-based authentication.
170
+ *
171
+ * @example
172
+ * ```typescript
173
+ * // Basic usage
174
+ * app.use('/api/protected', authService.authenticateToken());
175
+ *
176
+ * // With debug logging
177
+ * app.use('/api/protected', authService.authenticateToken({ debug: true }));
178
+ *
179
+ * // Without loading full user data (faster)
180
+ * app.use('/api/protected', authService.authenticateToken({ loadFullUser: false }));
181
+ *
182
+ * // With custom error handling
183
+ * app.use('/api/protected', authService.authenticateToken({
184
+ * onError: (error) => {
185
+ * console.error('Auth error:', error);
186
+ * // Custom error handling logic
187
+ * }
188
+ * }));
189
+ * ```
190
+ *
191
+ * @param options Configuration options
192
+ * @param options.loadFullUser Whether to load complete user data (default: true)
193
+ * @param options.onError Custom error handler function
194
+ * @param options.debug Enable debug logging (default: false)
195
+ * @returns Express middleware function
196
+ */
197
+ authenticateToken(options: {
198
+ loadFullUser?: boolean;
199
+ onError?: (error: ApiError) => any;
200
+ debug?: boolean;
201
+ } = {}) {
202
+ const { loadFullUser = true, onError, debug = false } = options;
203
+
204
+ return async (req: any, res: any, next: any) => {
205
+ const startTime = Date.now();
206
+
207
+ try {
208
+ // Extract token from Authorization header
209
+ const authHeader = req.headers['authorization'];
210
+ const token = authHeader?.startsWith('Bearer ') ? authHeader.substring(7) : null;
211
+
212
+ if (debug) {
213
+ console.log(`🔐 Auth Middleware: Processing request to ${req.method} ${req.path}`);
214
+ console.log(`🔐 Auth Middleware: Token present: ${!!token}`);
215
+ }
216
+
217
+ if (!token) {
218
+ const error = {
219
+ message: 'Access token required',
220
+ code: 'MISSING_TOKEN',
221
+ status: 401
222
+ };
223
+
224
+ if (debug) console.log(`❌ Auth Middleware: Missing token`);
225
+
226
+ if (onError) return onError(error);
227
+ return res.status(401).json(error);
228
+ }
229
+
230
+ // Decode and validate token
231
+ let decoded: JwtPayload;
232
+ try {
233
+ decoded = jwtDecode<JwtPayload>(token);
234
+
235
+ if (debug) {
236
+ console.log(`🔐 Auth Middleware: Token decoded successfully`);
237
+ console.log(`🔐 Auth Middleware: User ID: ${decoded.userId || decoded.id}`);
238
+ console.log(`🔐 Auth Middleware: Has session ID: ${!!decoded.sessionId}`);
239
+ }
240
+ } catch (decodeError) {
241
+ const error = {
242
+ message: 'Invalid token format',
243
+ code: 'INVALID_TOKEN_FORMAT',
244
+ status: 403
245
+ };
246
+
247
+ if (debug) console.log(`❌ Auth Middleware: Token decode failed:`, decodeError);
248
+
249
+ if (onError) return onError(error);
250
+ return res.status(403).json(error);
251
+ }
252
+
253
+ const userId = decoded.userId || decoded.id;
254
+ if (!userId) {
255
+ const error = {
256
+ message: 'Token missing user ID',
257
+ code: 'INVALID_TOKEN_PAYLOAD',
258
+ status: 403
259
+ };
260
+
261
+ if (debug) console.log(`❌ Auth Middleware: Token missing user ID`);
262
+
263
+ if (onError) return onError(error);
264
+ return res.status(403).json(error);
265
+ }
266
+
267
+ // Validate session or token
268
+ let isValid = false;
269
+ let user: User | null = null;
270
+
271
+ if (decoded.sessionId) {
272
+ // Session-based validation
273
+ if (debug) console.log(`🔐 Auth Middleware: Using session validation for session: ${decoded.sessionId}`);
274
+
275
+ try {
276
+ const validation = await this.validateSession(decoded.sessionId, {
277
+ useHeaderValidation: true
278
+ });
279
+ isValid = validation.valid;
280
+ user = validation.user;
281
+
282
+ if (debug) {
283
+ console.log(`🔐 Auth Middleware: Session validation result: ${isValid}`);
284
+ console.log(`🔐 Auth Middleware: User loaded: ${!!user}`);
285
+ }
286
+ } catch (sessionError) {
287
+ if (debug) console.log(`❌ Auth Middleware: Session validation failed:`, sessionError);
288
+ isValid = false;
289
+ }
290
+ } else {
291
+ // Legacy token validation
292
+ if (debug) console.log(`🔐 Auth Middleware: Using legacy token validation`);
293
+
294
+ try {
295
+ isValid = await this.validate();
296
+
297
+ if (isValid && loadFullUser) {
298
+ // Use minimal user data for performance - full user can be loaded separately if needed
299
+ user = { id: userId } as User;
300
+ }
301
+
302
+ if (debug) {
303
+ console.log(`🔐 Auth Middleware: Legacy validation result: ${isValid}`);
304
+ console.log(`🔐 Auth Middleware: User loaded: ${!!user}`);
305
+ }
306
+ } catch (validationError) {
307
+ if (debug) console.log(`❌ Auth Middleware: Legacy validation failed:`, validationError);
308
+ isValid = false;
309
+ }
310
+ }
311
+
312
+ if (!isValid) {
313
+ const error = {
314
+ message: 'Invalid or expired token',
315
+ code: 'INVALID_TOKEN',
316
+ status: 403
317
+ };
318
+
319
+ if (debug) console.log(`❌ Auth Middleware: Token validation failed`);
320
+
321
+ if (onError) return onError(error);
322
+ return res.status(403).json(error);
323
+ }
324
+
325
+ // Set request properties
326
+ req.userId = userId;
327
+ req.accessToken = token;
328
+ req.user = user || { id: userId };
329
+
330
+ if (debug) {
331
+ const duration = Date.now() - startTime;
332
+ console.log(`✅ Auth Middleware: Authentication successful in ${duration}ms`);
333
+ console.log(`✅ Auth Middleware: User ID: ${userId}`);
334
+ console.log(`✅ Auth Middleware: Full user loaded: ${loadFullUser && !!user}`);
335
+ }
336
+
337
+ next();
338
+ } catch (error) {
339
+ const duration = Date.now() - startTime;
340
+ const apiError = this.handleError(error);
341
+
342
+ if (debug) {
343
+ console.log(`❌ Auth Middleware: Unexpected error after ${duration}ms:`, error);
344
+ console.log(`❌ Auth Middleware: API Error:`, apiError);
345
+ }
346
+
347
+ if (onError) return onError(apiError);
348
+ return res.status(apiError.status || 500).json(apiError);
349
+ }
350
+ };
351
+ }
352
+
353
+ /**
354
+ * Helper method for validating tokens without Express middleware
355
+ *
356
+ * Use this method when you need to validate tokens programmatically
357
+ * outside of Express middleware context.
358
+ *
359
+ * @example
360
+ * ```typescript
361
+ * const result = await authService.validateToken(token);
362
+ * if (result.valid) {
363
+ * console.log('User ID:', result.userId);
364
+ * console.log('User data:', result.user);
365
+ * } else {
366
+ * console.log('Validation failed:', result.error);
367
+ * }
368
+ * ```
369
+ *
370
+ * @param token JWT token to validate
371
+ * @returns Validation result with user data if valid
372
+ */
373
+ async validateToken(token: string): Promise<{
374
+ valid: boolean;
375
+ userId?: string;
376
+ user?: any;
377
+ error?: string;
378
+ }> {
379
+ try {
380
+ if (!token) {
381
+ return {
382
+ valid: false,
383
+ error: 'Token is required'
384
+ };
385
+ }
386
+
387
+ // Decode token
388
+ let decoded: JwtPayload;
389
+ try {
390
+ decoded = jwtDecode<JwtPayload>(token);
391
+ } catch (decodeError) {
392
+ return {
393
+ valid: false,
394
+ error: 'Invalid token format'
395
+ };
396
+ }
397
+
398
+ const userId = decoded.userId || decoded.id;
399
+ if (!userId) {
400
+ return {
401
+ valid: false,
402
+ error: 'Token missing user ID'
403
+ };
404
+ }
405
+
406
+ // Validate based on token type
407
+ if (decoded.sessionId) {
408
+ // Session-based validation
409
+ try {
410
+ const validation = await this.validateSession(decoded.sessionId, {
411
+ useHeaderValidation: true
412
+ });
413
+ return {
414
+ valid: validation.valid,
415
+ userId,
416
+ user: validation.user,
417
+ error: validation.valid ? undefined : 'Invalid or expired session'
418
+ };
419
+ } catch (sessionError) {
420
+ return {
421
+ valid: false,
422
+ userId,
423
+ error: 'Session validation failed'
424
+ };
425
+ }
426
+ } else {
427
+ // Legacy token validation
428
+ try {
429
+ const isValid = await this.validate();
430
+ if (!isValid) {
431
+ return {
432
+ valid: false,
433
+ userId,
434
+ error: 'Invalid or expired token'
435
+ };
436
+ }
437
+
438
+ // Use minimal user data for performance
439
+ const user = { id: userId } as User;
440
+
441
+ return {
442
+ valid: true,
443
+ userId,
444
+ user
445
+ };
446
+ } catch (validationError) {
447
+ return {
448
+ valid: false,
449
+ userId,
450
+ error: 'Token validation failed'
451
+ };
452
+ }
453
+ }
454
+ } catch (error) {
455
+ return {
456
+ valid: false,
457
+ error: error instanceof Error ? error.message : 'Token validation failed'
458
+ };
459
+ }
460
+ }
461
+
462
+ /**
463
+ * Get device sessions (delegates to DeviceService)
464
+ */
465
+ async getDeviceSessions(sessionId: string): Promise<any[]> {
466
+ try {
467
+ const res = await this.getClient().get(`/session/device/sessions/${sessionId}`);
468
+ return res.data;
469
+ } catch (error) {
470
+ throw this.handleError(error);
471
+ }
472
+ }
473
+
474
+ /**
475
+ * Logout from all device sessions (delegates to DeviceService)
476
+ */
477
+ async logoutAllDeviceSessions(sessionId: string): Promise<void> {
478
+ try {
479
+ await this.getClient().delete(`/session/device/logout-all/${sessionId}`);
480
+ } catch (error) {
481
+ throw this.handleError(error);
482
+ }
483
+ }
484
+
485
+ /**
486
+ * Update device name (delegates to DeviceService)
487
+ */
488
+ async updateDeviceName(sessionId: string, deviceName: string): Promise<void> {
489
+ try {
490
+ await this.getClient().put(`/session/device/name/${sessionId}`, { deviceName });
491
+ } catch (error) {
492
+ throw this.handleError(error);
493
+ }
494
+ }
495
+
496
+ /**
497
+ * Check username availability
498
+ */
499
+ async checkUsernameAvailability(username: string): Promise<{ available: boolean; message: string }> {
500
+ try {
501
+ const res = await this.getClient().get(`/auth/check-username/${username}`);
502
+ return res.data;
503
+ } catch (error: any) {
504
+ // If the endpoint doesn't exist, fall back to basic validation
505
+ if (error.response?.status === 404) {
506
+ console.warn('Username validation endpoint not found, using fallback validation');
507
+ return { available: true, message: 'Username validation not available' };
508
+ }
509
+
510
+ // If it's a validation error (400), return the error message
511
+ if (error.response?.status === 400) {
512
+ return { available: false, message: error.response.data.message || 'Username not available' };
513
+ }
514
+
515
+ throw this.handleError(error);
516
+ }
517
+ }
518
+
519
+ /**
520
+ * Check email availability
521
+ */
522
+ async checkEmailAvailability(email: string): Promise<{ available: boolean; message: string }> {
523
+ try {
524
+ const res = await this.getClient().get(`/auth/check-email/${email}`);
525
+ return res.data;
526
+ } catch (error: any) {
527
+ // If the endpoint doesn't exist, fall back to basic validation
528
+ if (error.response?.status === 404) {
529
+ console.warn('Email validation endpoint not found, using fallback validation');
530
+ return { available: true, message: 'Email validation not available' };
531
+ }
532
+
533
+ // If it's a validation error (400), return the error message
534
+ if (error.response?.status === 400) {
535
+ return { available: false, message: error.response.data.message || 'Email not available' };
536
+ }
537
+
538
+ throw this.handleError(error);
539
+ }
540
+ }
541
+
542
+ // Note: getUserById and getUserProfileByUsername methods have been moved to UserService
543
+ // Use oxyServices.users.getUserById() and oxyServices.users.getProfileByUsername() instead
544
+ }
@@ -0,0 +1,55 @@
1
+ import { OxyServices } from '../OxyServices';
2
+ import {
3
+ DeviceSession,
4
+ DeviceSessionsResponse,
5
+ DeviceSessionLogoutResponse,
6
+ UpdateDeviceNameResponse
7
+ } from '../../models/interfaces';
8
+ import { buildSearchParams } from '../../utils/apiUtils';
9
+
10
+ /**
11
+ * Device service for handling device session management
12
+ */
13
+ export class DeviceService extends OxyServices {
14
+ /**
15
+ * Get device sessions
16
+ */
17
+ async getDeviceSessions(sessionId: string, deviceId?: string): Promise<DeviceSession[]> {
18
+ try {
19
+ const params = { deviceId };
20
+ const searchParams = buildSearchParams(params);
21
+
22
+ const res = await this.getClient().get(`/devices/sessions/${sessionId}?${searchParams.toString()}`);
23
+ return res.data;
24
+ } catch (error) {
25
+ throw this.handleError(error);
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Logout from all device sessions
31
+ */
32
+ async logoutAllDeviceSessions(sessionId: string, deviceId?: string, excludeCurrent?: boolean): Promise<DeviceSessionLogoutResponse> {
33
+ try {
34
+ const params = { deviceId, excludeCurrent };
35
+ const searchParams = buildSearchParams(params);
36
+
37
+ const res = await this.getClient().delete(`/devices/sessions/${sessionId}/logout-all?${searchParams.toString()}`);
38
+ return res.data;
39
+ } catch (error) {
40
+ throw this.handleError(error);
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Update device name
46
+ */
47
+ async updateDeviceName(sessionId: string, deviceName: string): Promise<UpdateDeviceNameResponse> {
48
+ try {
49
+ const res = await this.getClient().put(`/devices/sessions/${sessionId}/name`, { deviceName });
50
+ return res.data;
51
+ } catch (error) {
52
+ throw this.handleError(error);
53
+ }
54
+ }
55
+ }