-
Notifications
You must be signed in to change notification settings - Fork 406
DO NOT MERGE: Feat/cloud auth gates #6174
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0562d87
249e27a
4abdaa3
13b429a
82e9a22
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,8 +34,19 @@ | |
| return null | ||
| }) | ||
|
|
||
| <<<<<<< HEAD | ||
|
Check failure on line 37 in src/composables/auth/useCurrentUser.ts
|
||
| const onUserResolved = (callback: (user: AuthUserInfo) => void) => | ||
| whenever(resolvedUserInfo, callback, { immediate: true }) | ||
| ======= | ||
|
Check failure on line 40 in src/composables/auth/useCurrentUser.ts
|
||
| const onUserResolved = (callback: (user: AuthUserInfo) => void) => { | ||
| if (resolvedUserInfo.value) { | ||
| callback(resolvedUserInfo.value) | ||
| } | ||
|
|
||
| const stop = whenever(resolvedUserInfo, callback) | ||
| return () => stop() | ||
| } | ||
| >>>>>>> 775c856bf (port user ID expose hook from 6786d8e to cloud) | ||
|
Check failure on line 49 in src/composables/auth/useCurrentUser.ts
|
||
|
|
||
| const userDisplayName = computed(() => { | ||
| if (isApiKeyLogin.value) { | ||
|
|
@@ -132,7 +143,10 @@ | |
| resolvedUserInfo, | ||
| handleSignOut, | ||
| handleSignIn, | ||
| <<<<<<< HEAD | ||
|
Check failure on line 146 in src/composables/auth/useCurrentUser.ts
|
||
| handleDeleteAccount, | ||
| ======= | ||
|
Check failure on line 148 in src/composables/auth/useCurrentUser.ts
|
||
| >>>>>>> 775c856bf (port user ID expose hook from 6786d8e to cloud) | ||
|
Check failure on line 149 in src/composables/auth/useCurrentUser.ts
|
||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Take deletion |
||
| onUserResolved | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| import { FirebaseError } from 'firebase/app' | ||
| import { AuthErrorCodes } from 'firebase/auth' | ||
| import { ref } from 'vue' | ||
| import { useRouter } from 'vue-router' | ||
|
|
||
| import { useErrorHandling } from '@/composables/useErrorHandling' | ||
| import type { ErrorRecoveryStrategy } from '@/composables/useErrorHandling' | ||
|
|
@@ -18,6 +19,7 @@ import { usdToMicros } from '@/utils/formatUtil' | |
| export const useFirebaseAuthActions = () => { | ||
| const authStore = useFirebaseAuthStore() | ||
| const toastStore = useToastStore() | ||
| const router = useRouter() | ||
| const { wrapWithErrorHandlingAsync, toastErrorHandler } = useErrorHandling() | ||
|
|
||
| const accessError = ref(false) | ||
|
|
@@ -54,6 +56,12 @@ export const useFirebaseAuthActions = () => { | |
| detail: t('auth.signOut.successDetail'), | ||
| life: 5000 | ||
| }) | ||
|
|
||
| // Redirect to login page if we're on cloud domain | ||
| const hostname = window.location.hostname | ||
| if (hostname.includes('cloud.comfy.org')) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. short term: if(isCloud)...
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. longer term: |
||
| await router.push({ name: 'cloud-login' }) | ||
| } | ||
| }, reportError) | ||
|
|
||
| const sendPasswordReset = wrapWithErrorHandlingAsync( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2069,6 +2069,45 @@ | |
| "buttons": { | ||
| "Close": "Close" | ||
| } | ||
| <<<<<<< HEAD | ||
|
Check failure on line 2072 in src/locales/en/main.json
|
||
| ======= | ||
|
Check failure on line 2073 in src/locales/en/main.json
|
||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just resolve this to take the strings |
||
| }, | ||
| "waitlist": { | ||
| "title": "Cloud Waitlist", | ||
| "message": "You have been added to the waitlist. We will notify you when access is available." | ||
| }, | ||
| "forgotPassword": { | ||
| "title": "Forgot Password", | ||
| "instructions": "Enter your email address and we'll send you a link to reset your password.", | ||
| "emailLabel": "Email", | ||
| "emailPlaceholder": "Enter your email", | ||
| "sendResetLink": "Send reset link", | ||
| "backToLogin": "Back to login", | ||
| "didntReceiveEmail": "Didn't receive an email? Contact us at", | ||
| "passwordResetSent": "Password reset email sent", | ||
| "passwordResetError": "Failed to send password reset email. Please try again.", | ||
| "emailRequired": "Email is required" | ||
| }, | ||
| "privateBeta": { | ||
| "title": "Cloud is currently in private beta", | ||
| "desc": "Sign in to join the waitlist. We’ll notify you when it’s your turn. Already been notified? Sign in start using Cloud." | ||
| }, | ||
| "start": { | ||
| "title": "start creating in seconds", | ||
| "desc": "Zero setup required. Works on any device.", | ||
| "explain": "Generate multiple outputs at once. Share workflows with ease.", | ||
| "learnAboutButton": "Learn about Cloud", | ||
| "wantToRun": "Want to run comfyUI locally instead?", | ||
| "download": "Download ComfyUI" | ||
| }, | ||
| "checkingStatus": "Checking your account status...", | ||
| "retrying": "Retrying...", | ||
| "retry": "Try Again", | ||
| "authTimeout": { | ||
| "title": "Connection Taking Too Long", | ||
| "message": "We're having trouble connecting to ComfyUI Cloud. This could be due to a slow connection or temporary service issue.", | ||
| "restart": "Sign Out & Try Again" | ||
| >>>>>>> 18b3b11b9 (feat: add error handling and timeout recovery to cloud onboarding (#5573)) | ||
|
Check failure on line 2110 in src/locales/en/main.json
|
||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| import type { RouteRecordRaw } from 'vue-router' | ||
|
|
||
| export const cloudOnboardingRoutes: RouteRecordRaw[] = [ | ||
| { | ||
| path: '/cloud', | ||
| component: () => | ||
| import('@/platform/onboarding/cloud/components/CloudLayoutView.vue'), | ||
| children: [ | ||
| { | ||
| path: 'login', | ||
| name: 'cloud-login', | ||
| component: () => | ||
| import('@/platform/onboarding/cloud/CloudLoginView.vue') | ||
| }, | ||
| { | ||
| path: 'signup', | ||
| name: 'cloud-signup', | ||
| component: () => | ||
| import('@/platform/onboarding/cloud/CloudSignupView.vue') | ||
| }, | ||
| { | ||
| path: 'forgot-password', | ||
| name: 'cloud-forgot-password', | ||
| component: () => | ||
| import('@/platform/onboarding/cloud/CloudForgotPasswordView.vue') | ||
| }, | ||
| { | ||
| path: 'survey', | ||
| name: 'cloud-survey', | ||
| component: () => | ||
| import('@/platform/onboarding/cloud/CloudSurveyView.vue'), | ||
| meta: { requiresAuth: true } | ||
| }, | ||
| { | ||
| path: 'waitlist', | ||
| name: 'cloud-waitlist', | ||
| component: () => | ||
| import('@/platform/onboarding/cloud/CloudWaitlistView.vue'), | ||
| meta: { requiresAuth: true } | ||
| }, | ||
| { | ||
| path: 'user-check', | ||
| name: 'cloud-user-check', | ||
| component: () => | ||
| import('@/platform/onboarding/cloud/UserCheckView.vue'), | ||
| meta: { requiresAuth: true } | ||
| }, | ||
| { | ||
| path: 'invite-check', | ||
| name: 'cloud-invite-check', | ||
| component: () => | ||
| import('@/platform/onboarding/cloud/InviteCheckView.vue'), | ||
| meta: { requiresAuth: true } | ||
| }, | ||
| { | ||
| path: 'claim-invite', | ||
| name: 'cloud-claim-invite', | ||
| component: () => | ||
| import('@/platform/onboarding/cloud/CloudClaimInviteView.vue'), | ||
| meta: { requiresAuth: true } | ||
| }, | ||
| { | ||
| path: 'verify-email', | ||
| name: 'cloud-verify-email', | ||
| component: () => | ||
| import('@/platform/onboarding/cloud/CloudVerifyEmailView.vue') | ||
| }, | ||
| { | ||
| path: 'sorry-contact-support', | ||
| name: 'cloud-sorry-contact-support', | ||
| component: () => | ||
| import('@/platform/onboarding/cloud/CloudSorryContactSupportView.vue') | ||
| }, | ||
| { | ||
| path: 'auth-timeout', | ||
| name: 'cloud-auth-timeout', | ||
| component: () => | ||
| import('@/platform/onboarding/cloud/CloudAuthTimeoutView.vue') | ||
| } | ||
| ] | ||
| } | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| <template> | ||
| <div class="h-full flex items-center justify-center p-8"> | ||
| <div class="w-96 text-center"> | ||
| <h2 class="text-xl mb-4"> | ||
| {{ $t('cloudOnboarding.authTimeout.title') }} | ||
| </h2> | ||
| <p class="mb-6 text-gray-600"> | ||
| {{ $t('cloudOnboarding.authTimeout.message') }} | ||
| </p> | ||
|
|
||
| <div class="flex flex-col gap-3"> | ||
| <Button | ||
| :label="$t('cloudOnboarding.authTimeout.restart')" | ||
| class="w-full" | ||
| @click="handleRestart" | ||
| /> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </template> | ||
|
|
||
| <script setup lang="ts"> | ||
| import Button from 'primevue/button' | ||
| import { useRouter } from 'vue-router' | ||
|
|
||
| import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions' | ||
|
|
||
| const router = useRouter() | ||
| const { logout } = useFirebaseAuthActions() | ||
|
|
||
| const handleRestart = async () => { | ||
| await logout() | ||
| await router.replace({ name: 'cloud-login' }) | ||
| } | ||
| </script> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| <template> | ||
| <CloudLoginViewSkeleton v-if="skeletonType === 'login'" /> | ||
| <CloudSurveyViewSkeleton v-else-if="skeletonType === 'survey'" /> | ||
| <CloudWaitlistViewSkeleton v-else-if="skeletonType === 'waitlist'" /> | ||
| <div v-else-if="error" class="h-full flex items-center justify-center p-8"> | ||
| <div class="w-96 p-2 text-center"> | ||
| <p class="text-red-500 mb-4">{{ errorMessage }}</p> | ||
| <Button | ||
| :label=" | ||
| isRetrying | ||
| ? $t('cloudOnboarding.retrying') | ||
| : $t('cloudOnboarding.retry') | ||
| " | ||
| :loading="isRetrying" | ||
| class="w-full" | ||
| @click="handleRetry" | ||
| /> | ||
| </div> | ||
| </div> | ||
| <div v-else class="flex items-center justify-center min-h-screen"> | ||
| <div class="animate-pulse text-gray-500">{{ $t('g.loading') }}</div> | ||
| </div> | ||
| </template> | ||
|
|
||
| <script setup lang="ts"> | ||
| import { useAsyncState } from '@vueuse/core' | ||
| import Button from 'primevue/button' | ||
| import { computed, nextTick, ref } from 'vue' | ||
| import { useRouter } from 'vue-router' | ||
|
|
||
| import { getSurveyCompletedStatus, getUserCloudStatus } from '@/api/auth' | ||
| import { useErrorHandling } from '@/composables/useErrorHandling' | ||
|
|
||
| import CloudLoginViewSkeleton from './skeletons/CloudLoginViewSkeleton.vue' | ||
| import CloudSurveyViewSkeleton from './skeletons/CloudSurveyViewSkeleton.vue' | ||
| import CloudWaitlistViewSkeleton from './skeletons/CloudWaitlistViewSkeleton.vue' | ||
|
|
||
| const router = useRouter() | ||
| const { wrapWithErrorHandlingAsync } = useErrorHandling() | ||
|
|
||
| const skeletonType = ref<'login' | 'survey' | 'waitlist' | 'loading'>('loading') | ||
|
|
||
| const { | ||
| isLoading, | ||
| error, | ||
| execute: checkUserStatus | ||
| } = useAsyncState( | ||
| wrapWithErrorHandlingAsync(async () => { | ||
| await nextTick() | ||
|
|
||
| const [cloudUserStats, surveyStatus] = await Promise.all([ | ||
| getUserCloudStatus(), | ||
| getSurveyCompletedStatus() | ||
| ]) | ||
|
|
||
| // Navigate based on user status | ||
| if (!cloudUserStats) { | ||
| skeletonType.value = 'login' | ||
| await router.replace({ name: 'cloud-login' }) | ||
| return | ||
| } | ||
|
|
||
| if (!surveyStatus) { | ||
| skeletonType.value = 'survey' | ||
| await router.replace({ name: 'cloud-survey' }) | ||
| return | ||
| } | ||
|
|
||
| if (cloudUserStats.status !== 'active') { | ||
| skeletonType.value = 'waitlist' | ||
| await router.replace({ name: 'cloud-waitlist' }) | ||
| return | ||
| } | ||
|
|
||
| // User is fully onboarded | ||
| window.location.href = '/' | ||
| }), | ||
| null, | ||
| { resetOnExecute: false } | ||
| ) | ||
|
|
||
| const errorMessage = computed(() => { | ||
| if (!error.value) return '' | ||
|
|
||
| // Provide user-friendly error messages | ||
| const errorStr = error.value.toString().toLowerCase() | ||
|
|
||
| if (errorStr.includes('network') || errorStr.includes('fetch')) { | ||
| return 'Connection problem. Please check your internet connection.' | ||
| } | ||
|
|
||
| if (errorStr.includes('timeout')) { | ||
| return 'Request timed out. Please try again.' | ||
| } | ||
|
|
||
| return 'Unable to check account status. Please try again.' | ||
| }) | ||
|
|
||
| const isRetrying = computed(() => isLoading.value && !!error.value) | ||
|
|
||
| const handleRetry = async () => { | ||
| await checkUserStatus() | ||
| } | ||
| </script> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do note use this. take HEAD