mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-04 21:22:07 +00:00
feat: add email verification check for cloud onboarding (#5636)
## Summary
- Added email verification flow for new users during onboarding
- Implemented invite code claiming with proper validation
- Updated API endpoints from `/invite/` to `/invite_code/` for
consistency
## Changes
### Email Verification
- Added `CloudVerifyEmailView` component with email verification UI
- Added email verification check after login in `CloudLoginView`
- Added `isEmailVerified` property to Firebase auth store
- Users must verify email before claiming invite codes
### Invite Code Flow
- Enhanced `CloudClaimInviteView` with full claim invite functionality
- Updated `InviteCheckView` to route users based on email verification
status
- Modified API to return both `claimed` and `expired` status for invite
codes
- Added proper error handling and Sentry logging for invite operations
### API Updates
- Changed endpoint paths from `/invite/` to `/invite_code/`
- Updated `getInviteCodeStatus()` to return `{ claimed: boolean;
expired: boolean }`
- Updated `claimInvite()` to return `{ success: boolean; message: string
}`
### UI/UX Improvements
- Added Korean translations for all new strings
- Improved button styling and layout in survey and waitlist views
- Added proper loading states and error handling
## Test Plan
- [ ] Test new user signup flow with email verification
- [ ] Test invite code validation (expired/claimed/valid codes)
- [ ] Test email verification redirect flow
- [ ] Test invite claiming after email verification
- [ ] Verify Korean translations display correctly
🤖 Generated with [Claude Code](https://claude.ai/code)
---------
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,29 +1,44 @@
|
||||
<template>
|
||||
<div class="h-full flex items-center justify-center p-8">
|
||||
<div class="lg:w-96 max-w-[100vw] p-2">
|
||||
<div class="bg-[#2d2e32] p-4 rounded-lg">
|
||||
<h4 class="m-0 pb-2 text-lg">
|
||||
{{ t('cloudPrivateBeta_title') }}
|
||||
</h4>
|
||||
<p class="m-0 text-base leading-6">
|
||||
{{ t('cloudPrivateBeta_desc') }}
|
||||
</p>
|
||||
</div>
|
||||
<template v-if="!hasInviteCode">
|
||||
<div class="bg-[#2d2e32] p-4 rounded-lg">
|
||||
<h4 class="m-0 pb-2 text-lg">
|
||||
{{ t('cloudPrivateBeta_title') }}
|
||||
</h4>
|
||||
<p class="m-0 text-base leading-6">
|
||||
{{ t('cloudPrivateBeta_desc') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Header -->
|
||||
<div class="flex flex-col gap-4 mt-6 mb-8">
|
||||
<h1 class="text-xl font-medium leading-normal my-0">
|
||||
{{ t('auth.login.title') }}
|
||||
</h1>
|
||||
<p class="text-base my-0">
|
||||
<span class="text-muted">{{ t('auth.login.newUser') }}</span>
|
||||
<span
|
||||
class="ml-1 cursor-pointer text-blue-500"
|
||||
@click="navigateToSignup"
|
||||
>{{ t('auth.login.signUp') }}</span
|
||||
<!-- Header -->
|
||||
<div class="flex flex-col gap-4 mt-6 mb-8">
|
||||
<h1 class="text-xl font-medium leading-normal my-0">
|
||||
{{ t('auth.login.title') }}
|
||||
</h1>
|
||||
<p class="text-base my-0">
|
||||
<span class="text-muted">{{ t('auth.login.newUser') }}</span>
|
||||
<span
|
||||
class="ml-1 cursor-pointer text-blue-500"
|
||||
@click="navigateToSignup"
|
||||
>{{ t('auth.login.signUp') }}</span
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div class="flex flex-col gap-1 mt-6 mb-8">
|
||||
<h1
|
||||
class="text-white font-abcrom font-black italic uppercase my-0 text-2xl"
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
{{ t('cloudStart_invited') }}
|
||||
</h1>
|
||||
<p class="text-base my-0">
|
||||
<span class="text-muted">{{ t('cloudStart_invited_signin') }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<Message v-if="!isSecureContext" severity="warn" class="mb-4">
|
||||
{{ t('auth.login.insecureContextWarning') }}
|
||||
@@ -61,7 +76,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Terms & Contact -->
|
||||
<p class="mt-5 text-sm text-gray-600">
|
||||
<p v-if="!hasInviteCode" class="mt-5 text-sm text-gray-600">
|
||||
{{ t('cloudWaitlist_questionsText') }}
|
||||
<a
|
||||
href="https://support.comfy.org"
|
||||
@@ -72,6 +87,15 @@
|
||||
{{ t('cloudWaitlist_contactLink') }}</a
|
||||
>.
|
||||
</p>
|
||||
<p v-else class="mt-5 text-sm text-gray-600">
|
||||
{{ t('cloudStart_invited_signup_title') }}
|
||||
<span
|
||||
class="text-blue-400 no-underline cursor-pointer"
|
||||
@click="navigateToSignup"
|
||||
>
|
||||
{{ t('cloudStart_invited_signup_description') }}</span
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -80,13 +104,14 @@
|
||||
import Button from 'primevue/button'
|
||||
import Divider from 'primevue/divider'
|
||||
import Message from 'primevue/message'
|
||||
import { ref } from 'vue'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
|
||||
import CloudSignInForm from '@/platform/onboarding/cloud/components/CloudSignInForm.vue'
|
||||
import { type SignInData } from '@/schemas/signInSchema'
|
||||
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
||||
import { translateAuthError } from '@/utils/authErrorTranslation'
|
||||
|
||||
const { t } = useI18n()
|
||||
@@ -96,23 +121,29 @@ const authActions = useFirebaseAuthActions()
|
||||
const isSecureContext = window.isSecureContext
|
||||
const authError = ref('')
|
||||
|
||||
const hasInviteCode = computed(() => !!route.query.inviteCode)
|
||||
|
||||
const navigateToSignup = () => {
|
||||
void router.push({ name: 'cloud-signup', query: route.query })
|
||||
}
|
||||
|
||||
const onSuccess = async () => {
|
||||
// Check if there's an invite code
|
||||
const inviteCode = route.query.inviteCode as string
|
||||
|
||||
if (inviteCode) {
|
||||
// Handle invite code flow - go to invite check
|
||||
await router.push({
|
||||
name: 'cloud-invite-check',
|
||||
query: { inviteCode }
|
||||
})
|
||||
const inviteCode = route.query.inviteCode as string | undefined
|
||||
const { isEmailVerified } = useFirebaseAuthStore()
|
||||
if (!isEmailVerified) {
|
||||
await router.push({ name: 'cloud-verify-email', query: { inviteCode } })
|
||||
} else {
|
||||
// Normal login flow - go to user check
|
||||
await router.push({ name: 'cloud-user-check' })
|
||||
if (inviteCode) {
|
||||
// Handle invite code flow - go to invite check
|
||||
await router.push({
|
||||
name: 'cloud-invite-check',
|
||||
query: { inviteCode }
|
||||
})
|
||||
} else {
|
||||
// Normal login flow - go to user check
|
||||
await router.push({ name: 'cloud-user-check' })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,4 +179,8 @@ const signInWithEmail = async (values: SignInData) => {
|
||||
await onSuccess()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await authActions.logout()
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user