@onairos/react-native 3.0.49 → 3.0.51

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 (34) hide show
  1. package/lib/commonjs/components/EmailVerificationModal.js +317 -0
  2. package/lib/commonjs/components/EmailVerificationModal.js.map +1 -0
  3. package/lib/commonjs/components/TrainingModal.js +66 -75
  4. package/lib/commonjs/components/TrainingModal.js.map +1 -1
  5. package/lib/commonjs/components/UniversalOnboarding.js +30 -5
  6. package/lib/commonjs/components/UniversalOnboarding.js.map +1 -1
  7. package/lib/commonjs/index.js +60 -2
  8. package/lib/commonjs/index.js.map +1 -1
  9. package/lib/commonjs/services/platformAuthService.js +505 -53
  10. package/lib/commonjs/services/platformAuthService.js.map +1 -1
  11. package/lib/module/components/EmailVerificationModal.js +308 -0
  12. package/lib/module/components/EmailVerificationModal.js.map +1 -0
  13. package/lib/module/components/TrainingModal.js +66 -75
  14. package/lib/module/components/TrainingModal.js.map +1 -1
  15. package/lib/module/components/UniversalOnboarding.js +31 -6
  16. package/lib/module/components/UniversalOnboarding.js.map +1 -1
  17. package/lib/module/index.js +3 -1
  18. package/lib/module/index.js.map +1 -1
  19. package/lib/module/services/platformAuthService.js +496 -52
  20. package/lib/module/services/platformAuthService.js.map +1 -1
  21. package/lib/typescript/components/EmailVerificationModal.d.ts +10 -0
  22. package/lib/typescript/components/EmailVerificationModal.d.ts.map +1 -0
  23. package/lib/typescript/components/TrainingModal.d.ts.map +1 -1
  24. package/lib/typescript/components/UniversalOnboarding.d.ts.map +1 -1
  25. package/lib/typescript/index.d.ts +4 -1
  26. package/lib/typescript/index.d.ts.map +1 -1
  27. package/lib/typescript/services/platformAuthService.d.ts +57 -1
  28. package/lib/typescript/services/platformAuthService.d.ts.map +1 -1
  29. package/package.json +2 -1
  30. package/src/components/EmailVerificationModal.tsx +356 -0
  31. package/src/components/TrainingModal.tsx +69 -73
  32. package/src/components/UniversalOnboarding.tsx +31 -6
  33. package/src/index.ts +14 -1
  34. package/src/services/platformAuthService.ts +527 -55
@@ -1,5 +1,5 @@
1
1
  import { Platform, Linking } from 'react-native';
2
- import { DEEP_LINK_CONFIG } from '../constants';
2
+ import AsyncStorage from '@react-native-async-storage/async-storage';
3
3
 
4
4
  // Define types for platform auth configuration
5
5
  interface PlatformAuthConfig {
@@ -7,20 +7,28 @@ interface PlatformAuthConfig {
7
7
  nativeSDKPackage?: string;
8
8
  authEndpoint: string;
9
9
  color: string;
10
+ clientId?: string;
11
+ redirectUri?: string;
12
+ scope?: string;
13
+ responseType?: string;
10
14
  }
11
15
 
12
16
  // Configuration for each platform's authentication
13
- const PLATFORM_AUTH_CONFIG: Record<string, PlatformAuthConfig> = {
17
+ let PLATFORM_AUTH_CONFIG: Record<string, PlatformAuthConfig> = {
14
18
  instagram: {
15
19
  hasNativeSDK: false, // Instagram uses OAuth WebView flow
16
20
  authEndpoint: 'https://api2.onairos.uk/instagram/authorize',
17
21
  color: '#E1306C',
18
22
  },
19
23
  youtube: {
20
- hasNativeSDK: false, // Native Google Sign-In SDK enabled
24
+ hasNativeSDK: true, // Native Google Sign-In SDK enabled
21
25
  nativeSDKPackage: '@react-native-google-signin/google-signin',
22
26
  authEndpoint: 'https://api2.onairos.uk/youtube/authorize',
23
27
  color: '#FF0000',
28
+ clientId: '1030678346906-lovkuds2ouqmoc8eu5qpo98spa6edv4o.apps.googleusercontent.com',
29
+ redirectUri: 'onairosevents://auth/callback',
30
+ scope: 'https://www.googleapis.com/auth/youtube.readonly',
31
+ responseType: 'code',
24
32
  },
25
33
  reddit: {
26
34
  hasNativeSDK: false,
@@ -197,68 +205,190 @@ export const initiateOAuth = async (platform: string, username: string, appName?
197
205
  * @param platform The platform to authenticate with
198
206
  * @returns A Promise that resolves to the authentication result
199
207
  */
200
- export const initiateNativeAuth = async (platform: string): Promise<boolean> => {
201
- try {
202
- // Currently only YouTube (Google Sign-In) is supported
203
- if (platform === 'youtube') {
204
- console.log('Initiating native Google Sign-In for YouTube');
208
+ export const initiateNativeAuth = async (platform: string, username?: string): Promise<boolean> => {
209
+ if (platform === 'youtube') {
210
+ console.log('🔗 Initiating native Google Sign-In for YouTube');
211
+ try {
212
+ // Import Google Sign-In dynamically to avoid errors if not installed
213
+ const { GoogleSignin, statusCodes } = require('@react-native-google-signin/google-signin');
214
+
215
+ // Configure Google Sign-In
216
+ await GoogleSignin.configure({
217
+ webClientId: '1030678346906-lovkuds2ouqmoc8eu5qpo98spa6edv4o.apps.googleusercontent.com', // Replace with your web client ID
218
+ iosClientId: '1030678346906-lovkuds2ouqmoc8eu5qpo98spa6edv4o.apps.googleusercontent.com', // Replace with your iOS client ID
219
+ scopes: ['https://www.googleapis.com/auth/youtube.readonly'],
220
+ offlineAccess: true, // CRITICAL: This ensures we get refresh tokens
221
+ hostedDomain: '',
222
+ forceCodeForRefreshToken: true, // CRITICAL: Force refresh token on first sign-in
223
+ accountName: '', // Clear to avoid conflicts
224
+ });
225
+
226
+ // Check if Google Play Services are available
227
+ await GoogleSignin.hasPlayServices();
228
+
229
+ // Sign in with Google
230
+ const userInfo = await GoogleSignin.signIn();
231
+ console.log('✅ Google Sign-In successful:', userInfo.user?.email);
232
+
233
+ // Get access token for API calls
234
+ const tokens = await GoogleSignin.getTokens();
235
+ console.log('🔑 Got Google tokens');
205
236
 
237
+ // Get current user info with refresh token
238
+ const currentUser = await GoogleSignin.getCurrentUser();
239
+ console.log('👤 Current user info:', currentUser?.user?.email);
240
+
241
+ // Extract refresh token from server auth code
242
+ let refreshToken = null;
243
+ if (currentUser?.serverAuthCode) {
244
+ console.log('🔄 Server auth code available for refresh token');
245
+ refreshToken = currentUser.serverAuthCode;
246
+ }
247
+
248
+ if (!refreshToken) {
249
+ console.warn('⚠️ No refresh token available - token refresh may fail later');
250
+ } else {
251
+ console.log('✅ Refresh token available for YouTube connection');
252
+ }
253
+
254
+ // Fetch YouTube channel information
255
+ let channelName = 'Unknown Channel';
256
+ let channelId = null;
206
257
  try {
207
- // Import Google Sign-In dynamically to avoid errors if not installed
208
- const { GoogleSignin, statusCodes } = require('@react-native-google-signin/google-signin');
209
-
210
- // Configure Google Sign-In
211
- await GoogleSignin.configure({
212
- scopes: [
213
- 'https://www.googleapis.com/auth/youtube.readonly',
214
- 'https://www.googleapis.com/auth/youtube.force-ssl'
215
- ],
216
- webClientId: 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com', // Replace with your Web Client ID from Google Cloud Console
217
- iosClientId: 'YOUR_IOS_CLIENT_ID.apps.googleusercontent.com', // Replace with your iOS Client ID (iOS only)
218
- offlineAccess: true,
219
- hostedDomain: '',
220
- forceCodeForRefreshToken: true,
258
+ console.log('📺 Fetching YouTube channel information...');
259
+ const channelResponse = await fetch('https://www.googleapis.com/youtube/v3/channels?part=snippet&mine=true', {
260
+ headers: {
261
+ 'Authorization': `Bearer ${tokens.accessToken}`,
262
+ 'Accept': 'application/json'
263
+ }
221
264
  });
222
265
 
223
- // Check if device supports Google Play Services
224
- await GoogleSignin.hasPlayServices();
225
-
226
- // Sign in
227
- const userInfo = await GoogleSignin.signIn();
228
- console.log('Google Sign-In successful:', userInfo);
229
-
230
- // Get access token
231
- const tokens = await GoogleSignin.getTokens();
232
- console.log('Google tokens:', tokens);
233
-
234
- // Here you would typically send the tokens to your backend
235
- // to associate the YouTube account with the user
236
-
237
- return true;
238
- } catch (error: any) {
239
- console.error('Google Sign-In error:', error);
240
-
241
- const { statusCodes: StatusCodes } = require('@react-native-google-signin/google-signin');
242
-
243
- if (error.code === StatusCodes.SIGN_IN_CANCELLED) {
244
- console.log('User cancelled the sign-in flow');
245
- } else if (error.code === StatusCodes.IN_PROGRESS) {
246
- console.log('Sign-in is in progress already');
247
- } else if (error.code === StatusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
248
- console.log('Play services not available or outdated');
249
- } else {
250
- console.log('Some other error happened');
266
+ if (channelResponse.ok) {
267
+ const channelData = await channelResponse.json();
268
+ if (channelData.items && channelData.items.length > 0) {
269
+ channelName = channelData.items[0].snippet.title;
270
+ channelId = channelData.items[0].id;
271
+ console.log(' YouTube channel found:', channelName);
272
+ }
251
273
  }
274
+ } catch (channelError) {
275
+ console.log('⚠️ Error fetching YouTube channel info:', channelError);
276
+ channelName = userInfo.user?.name || userInfo.user?.email || 'Unknown Channel';
277
+ }
278
+
279
+ // Get authentication info
280
+ let authToken = await AsyncStorage.getItem('onairos_jwt_token') ||
281
+ await AsyncStorage.getItem('enoch_token') ||
282
+ await AsyncStorage.getItem('auth_token');
283
+ const storedUsername = await AsyncStorage.getItem('onairos_username');
284
+ const finalUsername = storedUsername || username || userInfo.user?.email || 'youtube_user';
285
+
286
+ // Create auth token if needed
287
+ if (!authToken || authToken.trim().length < 20) {
288
+ console.log('🔐 Creating authentication token for YouTube...');
252
289
 
290
+ try {
291
+ const fallbackEmail = userInfo.user?.email || `${finalUsername}@youtube.temp`;
292
+
293
+ // Create user accounts and get JWT token
294
+ const onairosSignupResponse = await fetch('https://api2.onairos.uk/register/enoch', {
295
+ method: 'POST',
296
+ headers: {
297
+ 'Content-Type': 'application/json'
298
+ },
299
+ body: JSON.stringify({
300
+ email: fallbackEmail,
301
+ username: finalUsername,
302
+ name: finalUsername
303
+ })
304
+ });
305
+
306
+ if (onairosSignupResponse.ok) {
307
+ const onairosResponseData = await onairosSignupResponse.json();
308
+
309
+ // Extract token from response
310
+ authToken = onairosResponseData.token || onairosResponseData.data?.token || onairosResponseData.jwt;
311
+
312
+ if (authToken) {
313
+ // Store tokens
314
+ await AsyncStorage.setItem('onairos_jwt_token', authToken);
315
+ await AsyncStorage.setItem('enoch_token', authToken);
316
+ await AsyncStorage.setItem('auth_token', authToken);
317
+ await AsyncStorage.setItem('onairos_username', onairosResponseData.username || finalUsername);
318
+
319
+ console.log('✅ Successfully created and stored authentication token');
320
+ }
321
+ }
322
+ } catch (tokenError) {
323
+ console.warn('⚠️ Error creating auth token:', tokenError);
324
+ }
325
+ }
326
+
327
+ console.log('🔗 Linking YouTube data to user:', finalUsername);
328
+ console.log('📺 YouTube channel name:', channelName);
329
+
330
+ // Send tokens to backend for YouTube data processing
331
+ const backendResponse = await fetch('https://api2.onairos.uk/youtube/native-auth', {
332
+ method: 'POST',
333
+ headers: {
334
+ 'Content-Type': 'application/json',
335
+ ...(authToken && { 'Authorization': `Bearer ${authToken}` })
336
+ },
337
+ body: JSON.stringify({
338
+ session: {
339
+ username: finalUsername,
340
+ platform: 'youtube',
341
+ timestamp: new Date().toISOString(),
342
+ channelName: channelName,
343
+ channelId: channelId
344
+ },
345
+ googleUser: userInfo.user,
346
+ accessToken: tokens.accessToken,
347
+ idToken: tokens.idToken,
348
+ refreshToken: refreshToken, // CRITICAL: Include refresh token
349
+ serverAuthCode: currentUser?.serverAuthCode,
350
+ userAccountInfo: {
351
+ username: finalUsername,
352
+ email: userInfo.user?.email,
353
+ authToken: authToken,
354
+ channelName: channelName,
355
+ channelId: channelId,
356
+ userIdentifier: authToken ? `user-${authToken.substring(0, 10)}` : `youtube-${userInfo.user?.email}`,
357
+ googleId: userInfo.user?.id,
358
+ },
359
+ tokenExpiry: new Date(Date.now() + 3600 * 1000).toISOString(), // 1 hour from now
360
+ requestRefreshToken: true
361
+ })
362
+ });
363
+
364
+ if (backendResponse.ok) {
365
+ const responseData = await backendResponse.json();
366
+ console.log('✅ YouTube connection processed by backend:', responseData);
367
+ return true;
368
+ } else {
369
+ const errorData = await backendResponse.text();
370
+ console.error('❌ Backend processing failed:', backendResponse.status, errorData);
253
371
  return false;
254
372
  }
373
+
374
+ } catch (error: any) {
375
+ console.error('❌ Google Sign-In error:', error);
376
+
377
+ const { statusCodes: StatusCodes } = require('@react-native-google-signin/google-signin');
378
+
379
+ if (error.code === StatusCodes.SIGN_IN_CANCELLED) {
380
+ console.log('User cancelled Google Sign-In');
381
+ } else if (error.code === StatusCodes.IN_PROGRESS) {
382
+ console.log('Google Sign-In already in progress');
383
+ } else if (error.code === StatusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
384
+ console.log('Google Play Services not available');
385
+ }
386
+
387
+ return false;
255
388
  }
256
-
257
- throw new Error(`Native authentication not supported for ${platform}`);
258
- } catch (error) {
259
- console.error(`Error initiating native auth for ${platform}:`, error);
260
- throw error;
261
389
  }
390
+
391
+ return false;
262
392
  };
263
393
 
264
394
  /**
@@ -317,3 +447,345 @@ export const testApiConnectivity = async (): Promise<{ success: boolean; error?:
317
447
  return { success: false, error: error instanceof Error ? error.message : 'Unknown error' };
318
448
  }
319
449
  };
450
+
451
+ /**
452
+ * 🔄 REFRESH GOOGLE TOKENS
453
+ */
454
+ export const refreshGoogleTokens = async (): Promise<{ accessToken: string; idToken?: string } | null> => {
455
+ try {
456
+ console.log('🔄 Attempting to refresh Google tokens...');
457
+
458
+ const { GoogleSignin } = require('@react-native-google-signin/google-signin');
459
+
460
+ const currentUser = await GoogleSignin.getCurrentUser();
461
+ if (!currentUser) {
462
+ console.log('❌ User not signed in to Google, cannot refresh tokens');
463
+ return null;
464
+ }
465
+
466
+ const tokens = await GoogleSignin.getTokens();
467
+ console.log('✅ Successfully refreshed Google tokens');
468
+
469
+ return {
470
+ accessToken: tokens.accessToken,
471
+ idToken: tokens.idToken
472
+ };
473
+ } catch (error) {
474
+ console.error('❌ Failed to refresh Google tokens:', error);
475
+
476
+ // If refresh fails, try to sign in again
477
+ try {
478
+ console.log('🔄 Refresh failed, attempting re-authentication...');
479
+ const { GoogleSignin } = require('@react-native-google-signin/google-signin');
480
+ const userInfo = await GoogleSignin.signIn();
481
+ const tokens = await GoogleSignin.getTokens();
482
+
483
+ console.log('✅ Re-authentication successful');
484
+ return {
485
+ accessToken: tokens.accessToken,
486
+ idToken: tokens.idToken
487
+ };
488
+ } catch (signInError) {
489
+ console.error('❌ Re-authentication also failed:', signInError);
490
+ return null;
491
+ }
492
+ }
493
+ };
494
+
495
+ /**
496
+ * 🔄 REFRESH YOUTUBE TOKENS
497
+ */
498
+ export const refreshYouTubeTokens = async (): Promise<boolean> => {
499
+ try {
500
+ console.log('🔄 Refreshing YouTube tokens...');
501
+
502
+ // Get fresh tokens from Google SDK
503
+ const freshTokens = await refreshGoogleTokens();
504
+ if (!freshTokens) {
505
+ console.error('❌ Failed to get fresh tokens from Google SDK');
506
+ return false;
507
+ }
508
+
509
+ // Get current user info
510
+ const { GoogleSignin } = require('@react-native-google-signin/google-signin');
511
+ const currentUser = await GoogleSignin.getCurrentUser();
512
+ if (!currentUser) {
513
+ console.error('❌ No current Google user found');
514
+ return false;
515
+ }
516
+
517
+ // Get stored auth token
518
+ const authToken = await AsyncStorage.getItem('onairos_jwt_token') ||
519
+ await AsyncStorage.getItem('enoch_token') ||
520
+ await AsyncStorage.getItem('auth_token');
521
+
522
+ if (!authToken) {
523
+ console.error('❌ No auth token found for YouTube refresh');
524
+ return false;
525
+ }
526
+
527
+ // Send refreshed tokens to backend
528
+ const refreshResponse = await fetch('https://api2.onairos.uk/youtube/refresh-token', {
529
+ method: 'POST',
530
+ headers: {
531
+ 'Content-Type': 'application/json',
532
+ 'Authorization': `Bearer ${authToken}`
533
+ },
534
+ body: JSON.stringify({
535
+ accessToken: freshTokens.accessToken,
536
+ idToken: freshTokens.idToken,
537
+ refreshToken: currentUser.serverAuthCode,
538
+ userEmail: currentUser.user?.email,
539
+ tokenExpiry: new Date(Date.now() + 3600 * 1000).toISOString(),
540
+ timestamp: new Date().toISOString()
541
+ })
542
+ });
543
+
544
+ if (refreshResponse.ok) {
545
+ const responseData = await refreshResponse.json();
546
+ console.log('✅ YouTube tokens refreshed successfully:', responseData);
547
+ return true;
548
+ } else {
549
+ const errorData = await refreshResponse.text();
550
+ console.error('❌ YouTube token refresh failed:', refreshResponse.status, errorData);
551
+ return false;
552
+ }
553
+ } catch (error) {
554
+ console.error('❌ Error refreshing YouTube tokens:', error);
555
+ return false;
556
+ }
557
+ };
558
+
559
+ /**
560
+ * 🎯 ENHANCED OAUTH CALLBACK HANDLER
561
+ */
562
+ export const handleOAuthCallbackUrl = (url: string): { platform?: string; code?: string; success: boolean } => {
563
+ try {
564
+ console.log('🔍 Processing OAuth callback URL:', url);
565
+
566
+ // Parse the URL
567
+ const parsedUrl = new URL(url);
568
+
569
+ // Extract platform and code
570
+ const platform = parsedUrl.searchParams.get('platform') ||
571
+ parsedUrl.pathname.split('/').find(segment =>
572
+ ['instagram', 'youtube', 'reddit', 'pinterest', 'email'].includes(segment)
573
+ );
574
+
575
+ const code = parsedUrl.searchParams.get('code');
576
+ const error = parsedUrl.searchParams.get('error');
577
+
578
+ if (error) {
579
+ console.error('❌ OAuth error in callback:', error);
580
+ return { success: false };
581
+ }
582
+
583
+ if (code && platform) {
584
+ console.log(`✅ OAuth callback processed: ${platform} with code: ${code.substring(0, 10)}...`);
585
+ return { platform, code, success: true };
586
+ }
587
+
588
+ console.warn('⚠️ OAuth callback missing platform or code');
589
+ return { success: false };
590
+ } catch (error) {
591
+ console.error('❌ Error processing OAuth callback:', error);
592
+ return { success: false };
593
+ }
594
+ };
595
+
596
+ /**
597
+ * 🔧 UPDATE GOOGLE CLIENT IDS
598
+ * Allows apps to configure their own Google client IDs
599
+ */
600
+ export const updateGoogleClientIds = (config: {
601
+ webClientId?: string;
602
+ iosClientId?: string;
603
+ }) => {
604
+ console.log('🔧 Updating Google client IDs configuration');
605
+
606
+ if (config.webClientId || config.iosClientId) {
607
+ // Update the YouTube configuration
608
+ PLATFORM_AUTH_CONFIG.youtube = {
609
+ ...PLATFORM_AUTH_CONFIG.youtube,
610
+ clientId: config.webClientId || PLATFORM_AUTH_CONFIG.youtube.clientId,
611
+ };
612
+
613
+ console.log('✅ Google client IDs updated successfully');
614
+ }
615
+ };
616
+
617
+ /**
618
+ * 📧 EMAIL VERIFICATION FUNCTIONS
619
+ * Using the correct Onairos email verification endpoints
620
+ */
621
+ export const requestEmailVerification = async (email: string): Promise<{
622
+ success: boolean;
623
+ message?: string;
624
+ error?: string;
625
+ }> => {
626
+ try {
627
+ console.log('📧 Requesting email verification for:', email);
628
+
629
+ const response = await fetch('https://api2.onairos.uk/email/verify', {
630
+ method: 'POST',
631
+ headers: {
632
+ 'Content-Type': 'application/json',
633
+ },
634
+ body: JSON.stringify({ email }),
635
+ });
636
+
637
+ const result = await response.json();
638
+
639
+ if (response.ok && result.success) {
640
+ console.log('✅ Email verification requested successfully');
641
+ console.log('🔍 Testing mode: Code logged to server console, but accepts any code');
642
+ return {
643
+ success: true,
644
+ message: result.message || 'Verification code sent to your email (testing mode: any code accepted)',
645
+ };
646
+ } else {
647
+ console.error('❌ Email verification request failed:', result.error);
648
+ return {
649
+ success: false,
650
+ error: result.error || 'Failed to send verification code',
651
+ };
652
+ }
653
+ } catch (error) {
654
+ console.error('❌ Email verification request error:', error);
655
+ return {
656
+ success: false,
657
+ error: error instanceof Error ? error.message : 'Network error',
658
+ };
659
+ }
660
+ };
661
+
662
+ export const verifyEmailCode = async (email: string, code: string): Promise<{
663
+ success: boolean;
664
+ message?: string;
665
+ error?: string;
666
+ }> => {
667
+ try {
668
+ console.log('🔍 Verifying email code for:', email);
669
+
670
+ const response = await fetch('https://api2.onairos.uk/email/verify/confirm', {
671
+ method: 'POST',
672
+ headers: {
673
+ 'Content-Type': 'application/json',
674
+ },
675
+ body: JSON.stringify({ email, code }),
676
+ });
677
+
678
+ const result = await response.json();
679
+
680
+ if (response.ok && result.success) {
681
+ console.log('✅ Email verification successful');
682
+ console.log('🔍 Testing mode: Any code accepted for verification');
683
+ return {
684
+ success: true,
685
+ message: result.message || 'Email verified successfully',
686
+ };
687
+ } else {
688
+ console.error('❌ Email verification failed:', result.error);
689
+ return {
690
+ success: false,
691
+ error: result.error || 'Invalid verification code',
692
+ };
693
+ }
694
+ } catch (error) {
695
+ console.error('❌ Email verification error:', error);
696
+ return {
697
+ success: false,
698
+ error: error instanceof Error ? error.message : 'Network error',
699
+ };
700
+ }
701
+ };
702
+
703
+ export const checkEmailVerificationStatus = async (email: string): Promise<{
704
+ success: boolean;
705
+ isPending?: boolean;
706
+ message?: string;
707
+ error?: string;
708
+ }> => {
709
+ try {
710
+ console.log('🔍 Checking email verification status for:', email);
711
+
712
+ const response = await fetch(`https://api2.onairos.uk/email/verify/status/${encodeURIComponent(email)}`, {
713
+ method: 'GET',
714
+ headers: {
715
+ 'Content-Type': 'application/json',
716
+ },
717
+ });
718
+
719
+ const result = await response.json();
720
+
721
+ if (response.ok) {
722
+ console.log('✅ Email verification status retrieved');
723
+ return {
724
+ success: true,
725
+ isPending: result.isPending || false,
726
+ message: result.message || 'Status retrieved successfully',
727
+ };
728
+ } else {
729
+ console.error('❌ Email verification status check failed:', result.error);
730
+ return {
731
+ success: false,
732
+ error: result.error || 'Failed to check verification status',
733
+ };
734
+ }
735
+ } catch (error) {
736
+ console.error('❌ Email verification status error:', error);
737
+ return {
738
+ success: false,
739
+ error: error instanceof Error ? error.message : 'Network error',
740
+ };
741
+ }
742
+ };
743
+
744
+ /**
745
+ * 🔌 UNIVERSAL PLATFORM DISCONNECTION
746
+ * Backend confirmed this endpoint is fully implemented
747
+ */
748
+ export const disconnectPlatform = async (platform: string, username: string): Promise<{
749
+ success: boolean;
750
+ message?: string;
751
+ error?: string;
752
+ }> => {
753
+ try {
754
+ console.log(`🔌 Disconnecting ${platform} for user:`, username);
755
+
756
+ const response = await fetch('https://api2.onairos.uk/revoke', {
757
+ method: 'POST',
758
+ headers: {
759
+ 'Content-Type': 'application/json',
760
+ },
761
+ body: JSON.stringify({
762
+ Info: {
763
+ connection: platform.charAt(0).toUpperCase() + platform.slice(1), // Capitalize platform name
764
+ username: username,
765
+ },
766
+ }),
767
+ });
768
+
769
+ const result = await response.json();
770
+
771
+ if (response.ok && result.success) {
772
+ console.log(`✅ ${platform} disconnected successfully`);
773
+ return {
774
+ success: true,
775
+ message: result.message || `${platform} disconnected successfully`,
776
+ };
777
+ } else {
778
+ console.error(`❌ ${platform} disconnection failed:`, result.error);
779
+ return {
780
+ success: false,
781
+ error: result.error || `Failed to disconnect ${platform}`,
782
+ };
783
+ }
784
+ } catch (error) {
785
+ console.error(`❌ ${platform} disconnection error:`, error);
786
+ return {
787
+ success: false,
788
+ error: error instanceof Error ? error.message : 'Network error',
789
+ };
790
+ }
791
+ };