mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-11 02:20:08 +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:
@@ -88,10 +88,10 @@ export async function getUserCloudStatus(): Promise<UserCloudStatus> {
|
||||
|
||||
export async function getInviteCodeStatus(
|
||||
inviteCode: string
|
||||
): Promise<{ expired: boolean }> {
|
||||
): Promise<{ claimed: boolean; expired: boolean }> {
|
||||
try {
|
||||
const response = await api.fetchApi(
|
||||
`/invite/${encodeURIComponent(inviteCode)}/status`,
|
||||
`/invite_code/${encodeURIComponent(inviteCode)}/status`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
@@ -105,22 +105,22 @@ export async function getInviteCodeStatus(
|
||||
)
|
||||
captureApiError(
|
||||
error,
|
||||
'/invite/{code}/status',
|
||||
'/invite_code/{code}/status',
|
||||
'http_error',
|
||||
response.status,
|
||||
undefined,
|
||||
{
|
||||
api: {
|
||||
method: 'GET',
|
||||
endpoint: `/invite/${inviteCode}/status`,
|
||||
endpoint: `/invite_code/${inviteCode}/status`,
|
||||
status_code: response.status,
|
||||
status_text: response.statusText
|
||||
},
|
||||
extra: {
|
||||
invite_code_length: inviteCode.length
|
||||
},
|
||||
route_template: '/invite/{code}/status',
|
||||
route_actual: `/invite/${inviteCode}/status`
|
||||
route_template: '/invite_code/{code}/status',
|
||||
route_actual: `/invite_code/${inviteCode}/status`
|
||||
}
|
||||
)
|
||||
throw error
|
||||
@@ -132,13 +132,13 @@ export async function getInviteCodeStatus(
|
||||
if (!isHttpError(error, 'Failed to get invite code status:')) {
|
||||
captureApiError(
|
||||
error as Error,
|
||||
'/invite/{code}/status',
|
||||
'/invite_code/{code}/status',
|
||||
'network_error',
|
||||
undefined,
|
||||
undefined,
|
||||
{
|
||||
route_template: '/invite/{code}/status',
|
||||
route_actual: `/invite/${inviteCode}/status`
|
||||
route_template: '/invite_code/{code}/status',
|
||||
route_actual: `/invite_code/${inviteCode}/status`
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -293,7 +293,9 @@ export async function submitSurvey(
|
||||
}
|
||||
}
|
||||
|
||||
export async function claimInvite(code: string): Promise<void> {
|
||||
export async function claimInvite(
|
||||
code: string
|
||||
): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
Sentry.addBreadcrumb({
|
||||
category: 'auth',
|
||||
@@ -305,7 +307,7 @@ export async function claimInvite(code: string): Promise<void> {
|
||||
})
|
||||
|
||||
const res = await api.fetchApi(
|
||||
`/invite/${encodeURIComponent(code)}/claim`,
|
||||
`/invite_code/${encodeURIComponent(code)}/claim`,
|
||||
{
|
||||
method: 'POST'
|
||||
}
|
||||
@@ -317,7 +319,7 @@ export async function claimInvite(code: string): Promise<void> {
|
||||
)
|
||||
captureApiError(
|
||||
error,
|
||||
'/invite/{code}/claim',
|
||||
'/invite_code/{code}/claim',
|
||||
'http_error',
|
||||
res.status,
|
||||
'claim_invite',
|
||||
@@ -327,8 +329,8 @@ export async function claimInvite(code: string): Promise<void> {
|
||||
status_code: res.status,
|
||||
status_text: res.statusText
|
||||
},
|
||||
route_template: '/invite/{code}/claim',
|
||||
route_actual: `/invite/${encodeURIComponent(code)}/claim`
|
||||
route_template: '/invite_code/{code}/claim',
|
||||
route_actual: `/invite_code/${encodeURIComponent(code)}/claim`
|
||||
}
|
||||
)
|
||||
throw error
|
||||
@@ -340,18 +342,20 @@ export async function claimInvite(code: string): Promise<void> {
|
||||
message: 'Invite claimed successfully',
|
||||
level: 'info'
|
||||
})
|
||||
|
||||
return res.json()
|
||||
} catch (error) {
|
||||
// Only capture network errors (not HTTP errors we already captured)
|
||||
if (!isHttpError(error, 'Failed to claim invite:')) {
|
||||
captureApiError(
|
||||
error as Error,
|
||||
'/invite/{code}/claim',
|
||||
'/invite_code/{code}/claim',
|
||||
'network_error',
|
||||
undefined,
|
||||
'claim_invite',
|
||||
{
|
||||
route_template: '/invite/{code}/claim',
|
||||
route_actual: `/invite/${encodeURIComponent(code)}/claim`
|
||||
route_template: '/invite_code/{code}/claim',
|
||||
route_actual: `/invite_code/${encodeURIComponent(code)}/claim`
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user