mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-30 11:11:53 +00:00
[API Node] Show user state when logged in via API key (#3838)
Co-authored-by: github-actions <github-actions@github.com> Co-authored-by: Chenlei Hu <hcl@comfy.org>
This commit is contained in:
@@ -5,10 +5,10 @@
|
|||||||
<Divider class="mb-3" />
|
<Divider class="mb-3" />
|
||||||
|
|
||||||
<!-- Normal User Panel -->
|
<!-- Normal User Panel -->
|
||||||
<div v-if="user" class="flex flex-col gap-2">
|
<div v-if="isLoggedIn" class="flex flex-col gap-2">
|
||||||
<UserAvatar
|
<UserAvatar
|
||||||
v-if="user.photoURL"
|
v-if="userPhotoUrl"
|
||||||
:photo-url="user.photoURL"
|
:photo-url="userPhotoUrl"
|
||||||
shape="circle"
|
shape="circle"
|
||||||
size="large"
|
size="large"
|
||||||
/>
|
/>
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
{{ $t('userSettings.name') }}
|
{{ $t('userSettings.name') }}
|
||||||
</h3>
|
</h3>
|
||||||
<div class="text-muted">
|
<div class="text-muted">
|
||||||
{{ user.displayName || $t('userSettings.notSet') }}
|
{{ userDisplayName || $t('userSettings.notSet') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -26,8 +26,8 @@
|
|||||||
<h3 class="font-medium">
|
<h3 class="font-medium">
|
||||||
{{ $t('userSettings.email') }}
|
{{ $t('userSettings.email') }}
|
||||||
</h3>
|
</h3>
|
||||||
<a :href="'mailto:' + user.email" class="hover:underline">
|
<a :href="'mailto:' + userEmail" class="hover:underline">
|
||||||
{{ user.email }}
|
{{ userEmail }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -67,27 +67,6 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- API Key Panel -->
|
|
||||||
<div v-else-if="hasApiKey" class="flex flex-col gap-4">
|
|
||||||
<div class="flex flex-col gap-0.5">
|
|
||||||
<h3 class="font-medium">
|
|
||||||
{{ $t('auth.apiKey.title') }}
|
|
||||||
</h3>
|
|
||||||
<div class="text-muted flex items-center gap-1">
|
|
||||||
<i class="pi pi-key" />
|
|
||||||
{{ $t('auth.apiKey.label') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
class="mt-4 w-32"
|
|
||||||
severity="secondary"
|
|
||||||
:label="$t('auth.signOut.signOut')"
|
|
||||||
icon="pi pi-sign-out"
|
|
||||||
@click="handleApiKeySignOut"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Login Section -->
|
<!-- Login Section -->
|
||||||
<div v-else class="flex flex-col gap-4">
|
<div v-else class="flex flex-col gap-4">
|
||||||
<p class="text-gray-600">
|
<p class="text-gray-600">
|
||||||
@@ -112,59 +91,22 @@ import Button from 'primevue/button'
|
|||||||
import Divider from 'primevue/divider'
|
import Divider from 'primevue/divider'
|
||||||
import ProgressSpinner from 'primevue/progressspinner'
|
import ProgressSpinner from 'primevue/progressspinner'
|
||||||
import TabPanel from 'primevue/tabpanel'
|
import TabPanel from 'primevue/tabpanel'
|
||||||
import { computed } from 'vue'
|
|
||||||
|
|
||||||
import UserAvatar from '@/components/common/UserAvatar.vue'
|
import UserAvatar from '@/components/common/UserAvatar.vue'
|
||||||
|
import { useCurrentUser } from '@/composables/auth/useCurrentUser'
|
||||||
import { useDialogService } from '@/services/dialogService'
|
import { useDialogService } from '@/services/dialogService'
|
||||||
import { useApiKeyAuthStore } from '@/stores/apiKeyAuthStore'
|
|
||||||
import { useCommandStore } from '@/stores/commandStore'
|
|
||||||
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
|
||||||
|
|
||||||
const dialogService = useDialogService()
|
const dialogService = useDialogService()
|
||||||
const authStore = useFirebaseAuthStore()
|
const {
|
||||||
const commandStore = useCommandStore()
|
loading,
|
||||||
const apiKeyStore = useApiKeyAuthStore()
|
isLoggedIn,
|
||||||
|
isEmailProvider,
|
||||||
const user = computed(() => authStore.currentUser)
|
userDisplayName,
|
||||||
const loading = computed(() => authStore.loading)
|
userEmail,
|
||||||
const hasApiKey = computed(() => apiKeyStore.hasApiKey)
|
userPhotoUrl,
|
||||||
|
providerName,
|
||||||
const providerName = computed(() => {
|
providerIcon,
|
||||||
const providerId = user.value?.providerData[0]?.providerId
|
handleSignOut,
|
||||||
if (providerId?.includes('google')) {
|
handleSignIn
|
||||||
return 'Google'
|
} = useCurrentUser()
|
||||||
}
|
|
||||||
if (providerId?.includes('github')) {
|
|
||||||
return 'GitHub'
|
|
||||||
}
|
|
||||||
return providerId
|
|
||||||
})
|
|
||||||
|
|
||||||
const providerIcon = computed(() => {
|
|
||||||
const providerId = user.value?.providerData[0]?.providerId
|
|
||||||
if (providerId?.includes('google')) {
|
|
||||||
return 'pi pi-google'
|
|
||||||
}
|
|
||||||
if (providerId?.includes('github')) {
|
|
||||||
return 'pi pi-github'
|
|
||||||
}
|
|
||||||
return 'pi pi-user'
|
|
||||||
})
|
|
||||||
|
|
||||||
const isEmailProvider = computed(() => {
|
|
||||||
const providerId = user.value?.providerData[0]?.providerId
|
|
||||||
return providerId === 'password'
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleSignOut = async () => {
|
|
||||||
await commandStore.execute('Comfy.User.SignOut')
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleApiKeySignOut = async () => {
|
|
||||||
await apiKeyStore.clearStoredApiKey()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSignIn = async () => {
|
|
||||||
await commandStore.execute('Comfy.User.OpenSignInDialog')
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
v-if="isAuthenticated"
|
v-if="isLoggedIn"
|
||||||
class="user-profile-button p-1"
|
class="user-profile-button p-1"
|
||||||
severity="secondary"
|
severity="secondary"
|
||||||
text
|
text
|
||||||
@@ -30,15 +30,14 @@ import Popover from 'primevue/popover'
|
|||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
import UserAvatar from '@/components/common/UserAvatar.vue'
|
import UserAvatar from '@/components/common/UserAvatar.vue'
|
||||||
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
import { useCurrentUser } from '@/composables/auth/useCurrentUser'
|
||||||
|
|
||||||
import CurrentUserPopover from './CurrentUserPopover.vue'
|
import CurrentUserPopover from './CurrentUserPopover.vue'
|
||||||
|
|
||||||
const authStore = useFirebaseAuthStore()
|
const { isLoggedIn, userPhotoUrl } = useCurrentUser()
|
||||||
|
|
||||||
const popover = ref<InstanceType<typeof Popover> | null>(null)
|
const popover = ref<InstanceType<typeof Popover> | null>(null)
|
||||||
const isAuthenticated = computed(() => authStore.isAuthenticated)
|
|
||||||
const photoURL = computed<string | undefined>(
|
const photoURL = computed<string | undefined>(
|
||||||
() => authStore.currentUser?.photoURL ?? undefined
|
() => userPhotoUrl.value ?? undefined
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -6,19 +6,19 @@
|
|||||||
<div class="flex flex-col items-center">
|
<div class="flex flex-col items-center">
|
||||||
<UserAvatar
|
<UserAvatar
|
||||||
class="mb-3"
|
class="mb-3"
|
||||||
:photo-url="user?.photoURL"
|
:photo-url="userPhotoUrl"
|
||||||
:pt:icon:class="{
|
:pt:icon:class="{
|
||||||
'!text-2xl': !user?.photoURL
|
'!text-2xl': !userPhotoUrl
|
||||||
}"
|
}"
|
||||||
size="large"
|
size="large"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- User Details -->
|
<!-- User Details -->
|
||||||
<h3 class="text-lg font-semibold truncate my-0 mb-1">
|
<h3 class="text-lg font-semibold truncate my-0 mb-1">
|
||||||
{{ user?.displayName || $t('g.user') }}
|
{{ userDisplayName || $t('g.user') }}
|
||||||
</h3>
|
</h3>
|
||||||
<p v-if="user?.email" class="text-sm text-muted truncate my-0">
|
<p v-if="userEmail" class="text-sm text-muted truncate my-0">
|
||||||
{{ user.email }}
|
{{ userEmail }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -64,20 +64,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Button from 'primevue/button'
|
import Button from 'primevue/button'
|
||||||
import Divider from 'primevue/divider'
|
import Divider from 'primevue/divider'
|
||||||
import { computed, onMounted } from 'vue'
|
import { onMounted } from 'vue'
|
||||||
|
|
||||||
import UserAvatar from '@/components/common/UserAvatar.vue'
|
import UserAvatar from '@/components/common/UserAvatar.vue'
|
||||||
import UserCredit from '@/components/common/UserCredit.vue'
|
import UserCredit from '@/components/common/UserCredit.vue'
|
||||||
|
import { useCurrentUser } from '@/composables/auth/useCurrentUser'
|
||||||
import { useDialogService } from '@/services/dialogService'
|
import { useDialogService } from '@/services/dialogService'
|
||||||
import { useFirebaseAuthService } from '@/services/firebaseAuthService'
|
import { useFirebaseAuthService } from '@/services/firebaseAuthService'
|
||||||
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
|
||||||
|
|
||||||
const authStore = useFirebaseAuthStore()
|
const { userDisplayName, userEmail, userPhotoUrl } = useCurrentUser()
|
||||||
const authService = useFirebaseAuthService()
|
const authService = useFirebaseAuthService()
|
||||||
const dialogService = useDialogService()
|
const dialogService = useDialogService()
|
||||||
|
|
||||||
const user = computed(() => authStore.currentUser)
|
|
||||||
|
|
||||||
const handleOpenUserSettings = () => {
|
const handleOpenUserSettings = () => {
|
||||||
dialogService.showSettingsDialog('user')
|
dialogService.showSettingsDialog('user')
|
||||||
}
|
}
|
||||||
|
|||||||
101
src/composables/auth/useCurrentUser.ts
Normal file
101
src/composables/auth/useCurrentUser.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
import { useApiKeyAuthStore } from '@/stores/apiKeyAuthStore'
|
||||||
|
import { useCommandStore } from '@/stores/commandStore'
|
||||||
|
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
||||||
|
|
||||||
|
export const useCurrentUser = () => {
|
||||||
|
const authStore = useFirebaseAuthStore()
|
||||||
|
const commandStore = useCommandStore()
|
||||||
|
const apiKeyStore = useApiKeyAuthStore()
|
||||||
|
|
||||||
|
const firebaseUser = computed(() => authStore.currentUser)
|
||||||
|
const isApiKeyLogin = computed(() => apiKeyStore.isAuthenticated)
|
||||||
|
const isLoggedIn = computed(
|
||||||
|
() => !!isApiKeyLogin.value || firebaseUser.value !== null
|
||||||
|
)
|
||||||
|
|
||||||
|
const userDisplayName = computed(() => {
|
||||||
|
if (isApiKeyLogin.value) {
|
||||||
|
return apiKeyStore.currentUser?.name
|
||||||
|
}
|
||||||
|
return firebaseUser.value?.displayName
|
||||||
|
})
|
||||||
|
|
||||||
|
const userEmail = computed(() => {
|
||||||
|
if (isApiKeyLogin.value) {
|
||||||
|
return apiKeyStore.currentUser?.email
|
||||||
|
}
|
||||||
|
return firebaseUser.value?.email
|
||||||
|
})
|
||||||
|
|
||||||
|
const providerName = computed(() => {
|
||||||
|
if (isApiKeyLogin.value) {
|
||||||
|
return 'Comfy API Key'
|
||||||
|
}
|
||||||
|
|
||||||
|
const providerId = firebaseUser.value?.providerData[0]?.providerId
|
||||||
|
if (providerId?.includes('google')) {
|
||||||
|
return 'Google'
|
||||||
|
}
|
||||||
|
if (providerId?.includes('github')) {
|
||||||
|
return 'GitHub'
|
||||||
|
}
|
||||||
|
return providerId
|
||||||
|
})
|
||||||
|
|
||||||
|
const providerIcon = computed(() => {
|
||||||
|
if (isApiKeyLogin.value) {
|
||||||
|
return 'pi pi-key'
|
||||||
|
}
|
||||||
|
|
||||||
|
const providerId = firebaseUser.value?.providerData[0]?.providerId
|
||||||
|
if (providerId?.includes('google')) {
|
||||||
|
return 'pi pi-google'
|
||||||
|
}
|
||||||
|
if (providerId?.includes('github')) {
|
||||||
|
return 'pi pi-github'
|
||||||
|
}
|
||||||
|
return 'pi pi-user'
|
||||||
|
})
|
||||||
|
|
||||||
|
const isEmailProvider = computed(() => {
|
||||||
|
if (isApiKeyLogin.value) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const providerId = firebaseUser.value?.providerData[0]?.providerId
|
||||||
|
return providerId === 'password'
|
||||||
|
})
|
||||||
|
|
||||||
|
const userPhotoUrl = computed(() => {
|
||||||
|
if (isApiKeyLogin.value) return null
|
||||||
|
return firebaseUser.value?.photoURL
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleSignOut = async () => {
|
||||||
|
if (isApiKeyLogin.value) {
|
||||||
|
await apiKeyStore.clearStoredApiKey()
|
||||||
|
} else {
|
||||||
|
await commandStore.execute('Comfy.User.SignOut')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSignIn = async () => {
|
||||||
|
await commandStore.execute('Comfy.User.OpenSignInDialog')
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
loading: authStore.loading,
|
||||||
|
isLoggedIn,
|
||||||
|
isApiKeyLogin,
|
||||||
|
isEmailProvider,
|
||||||
|
userDisplayName,
|
||||||
|
userEmail,
|
||||||
|
userPhotoUrl,
|
||||||
|
providerName,
|
||||||
|
providerIcon,
|
||||||
|
handleSignOut,
|
||||||
|
handleSignIn
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,13 +7,14 @@ import {
|
|||||||
} from 'vue'
|
} from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
|
||||||
import { SettingTreeNode, useSettingStore } from '@/stores/settingStore'
|
import { SettingTreeNode, useSettingStore } from '@/stores/settingStore'
|
||||||
import type { SettingParams } from '@/types/settingTypes'
|
import type { SettingParams } from '@/types/settingTypes'
|
||||||
import { isElectron } from '@/utils/envUtil'
|
import { isElectron } from '@/utils/envUtil'
|
||||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
import { normalizeI18nKey } from '@/utils/formatUtil'
|
||||||
import { buildTree } from '@/utils/treeUtil'
|
import { buildTree } from '@/utils/treeUtil'
|
||||||
|
|
||||||
|
import { useCurrentUser } from '../auth/useCurrentUser'
|
||||||
|
|
||||||
interface SettingPanelItem {
|
interface SettingPanelItem {
|
||||||
node: SettingTreeNode
|
node: SettingTreeNode
|
||||||
component: Component
|
component: Component
|
||||||
@@ -29,7 +30,7 @@ export function useSettingUI(
|
|||||||
| 'credits'
|
| 'credits'
|
||||||
) {
|
) {
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const firebaseAuthStore = useFirebaseAuthStore()
|
const { isLoggedIn } = useCurrentUser()
|
||||||
const settingStore = useSettingStore()
|
const settingStore = useSettingStore()
|
||||||
const activeCategory = ref<SettingTreeNode | null>(null)
|
const activeCategory = ref<SettingTreeNode | null>(null)
|
||||||
|
|
||||||
@@ -165,7 +166,7 @@ export function useSettingUI(
|
|||||||
label: 'Account',
|
label: 'Account',
|
||||||
children: [
|
children: [
|
||||||
userPanel.node,
|
userPanel.node,
|
||||||
...(firebaseAuthStore.isAuthenticated ? [creditsPanel.node] : [])
|
...(isLoggedIn.value ? [creditsPanel.node] : [])
|
||||||
].map(translateCategory)
|
].map(translateCategory)
|
||||||
},
|
},
|
||||||
// Normal settings stored in the settingStore
|
// Normal settings stored in the settingStore
|
||||||
|
|||||||
@@ -1308,7 +1308,8 @@
|
|||||||
"success": "Login successful",
|
"success": "Login successful",
|
||||||
"failed": "Login failed",
|
"failed": "Login failed",
|
||||||
"insecureContextWarning": "This connection is insecure (HTTP) - your credentials may be intercepted by attackers if you proceed to login.",
|
"insecureContextWarning": "This connection is insecure (HTTP) - your credentials may be intercepted by attackers if you proceed to login.",
|
||||||
"questionsContactPrefix": "Questions? Contact us at"
|
"questionsContactPrefix": "Questions? Contact us at",
|
||||||
|
"noAssociatedUser": "There is no Comfy user associated with the provided API key"
|
||||||
},
|
},
|
||||||
"signup": {
|
"signup": {
|
||||||
"title": "Create an account",
|
"title": "Create an account",
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
"loginWithGithub": "Iniciar sesión con Github",
|
"loginWithGithub": "Iniciar sesión con Github",
|
||||||
"loginWithGoogle": "Iniciar sesión con Google",
|
"loginWithGoogle": "Iniciar sesión con Google",
|
||||||
"newUser": "¿Eres nuevo aquí?",
|
"newUser": "¿Eres nuevo aquí?",
|
||||||
|
"noAssociatedUser": "No hay ningún usuario de Comfy asociado con la clave API proporcionada",
|
||||||
"orContinueWith": "O continuar con",
|
"orContinueWith": "O continuar con",
|
||||||
"passwordLabel": "Contraseña",
|
"passwordLabel": "Contraseña",
|
||||||
"passwordPlaceholder": "Ingresa tu contraseña",
|
"passwordPlaceholder": "Ingresa tu contraseña",
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
"loginWithGithub": "Se connecter avec Github",
|
"loginWithGithub": "Se connecter avec Github",
|
||||||
"loginWithGoogle": "Se connecter avec Google",
|
"loginWithGoogle": "Se connecter avec Google",
|
||||||
"newUser": "Nouveau ici?",
|
"newUser": "Nouveau ici?",
|
||||||
|
"noAssociatedUser": "Aucun utilisateur Comfy n'est associé à la clé API fournie",
|
||||||
"orContinueWith": "Ou continuer avec",
|
"orContinueWith": "Ou continuer avec",
|
||||||
"passwordLabel": "Mot de passe",
|
"passwordLabel": "Mot de passe",
|
||||||
"passwordPlaceholder": "Entrez votre mot de passe",
|
"passwordPlaceholder": "Entrez votre mot de passe",
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
"loginWithGithub": "Githubでログイン",
|
"loginWithGithub": "Githubでログイン",
|
||||||
"loginWithGoogle": "Googleでログイン",
|
"loginWithGoogle": "Googleでログイン",
|
||||||
"newUser": "新規ユーザーですか?",
|
"newUser": "新規ユーザーですか?",
|
||||||
|
"noAssociatedUser": "指定されたAPIキーに関連付けられたComfyユーザーが存在しません",
|
||||||
"orContinueWith": "または以下で続ける",
|
"orContinueWith": "または以下で続ける",
|
||||||
"passwordLabel": "パスワード",
|
"passwordLabel": "パスワード",
|
||||||
"passwordPlaceholder": "パスワードを入力してください",
|
"passwordPlaceholder": "パスワードを入力してください",
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
"loginWithGithub": "Github로 로그인",
|
"loginWithGithub": "Github로 로그인",
|
||||||
"loginWithGoogle": "구글로 로그인",
|
"loginWithGoogle": "구글로 로그인",
|
||||||
"newUser": "처음이신가요?",
|
"newUser": "처음이신가요?",
|
||||||
|
"noAssociatedUser": "제공된 API 키와 연결된 Comfy 사용자가 없습니다",
|
||||||
"orContinueWith": "또는 다음으로 계속",
|
"orContinueWith": "또는 다음으로 계속",
|
||||||
"passwordLabel": "비밀번호",
|
"passwordLabel": "비밀번호",
|
||||||
"passwordPlaceholder": "비밀번호를 입력하세요",
|
"passwordPlaceholder": "비밀번호를 입력하세요",
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
"loginWithGithub": "Войти через Github",
|
"loginWithGithub": "Войти через Github",
|
||||||
"loginWithGoogle": "Войти через Google",
|
"loginWithGoogle": "Войти через Google",
|
||||||
"newUser": "Вы здесь впервые?",
|
"newUser": "Вы здесь впервые?",
|
||||||
|
"noAssociatedUser": "С предоставленным API-ключом не связан ни один пользователь Comfy",
|
||||||
"orContinueWith": "Или продолжить с",
|
"orContinueWith": "Или продолжить с",
|
||||||
"passwordLabel": "Пароль",
|
"passwordLabel": "Пароль",
|
||||||
"passwordPlaceholder": "Введите ваш пароль",
|
"passwordPlaceholder": "Введите ваш пароль",
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
"loginWithGithub": "使用Github登录",
|
"loginWithGithub": "使用Github登录",
|
||||||
"loginWithGoogle": "使用Google登录",
|
"loginWithGoogle": "使用Google登录",
|
||||||
"newUser": "新来的?",
|
"newUser": "新来的?",
|
||||||
|
"noAssociatedUser": "所提供的 API 密钥未关联任何 Comfy 用户",
|
||||||
"orContinueWith": "或者继续使用",
|
"orContinueWith": "或者继续使用",
|
||||||
"passwordLabel": "密码",
|
"passwordLabel": "密码",
|
||||||
"passwordPlaceholder": "输入您的密码",
|
"passwordPlaceholder": "输入您的密码",
|
||||||
|
|||||||
@@ -1,18 +1,51 @@
|
|||||||
import { useLocalStorage } from '@vueuse/core'
|
import { useLocalStorage } from '@vueuse/core'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { computed } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
|
|
||||||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||||||
import { t } from '@/i18n'
|
import { t } from '@/i18n'
|
||||||
|
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
||||||
import { useToastStore } from '@/stores/toastStore'
|
import { useToastStore } from '@/stores/toastStore'
|
||||||
|
import { ApiKeyAuthHeader } from '@/types/authTypes'
|
||||||
|
import { operations } from '@/types/comfyRegistryTypes'
|
||||||
|
|
||||||
|
type ComfyApiUser =
|
||||||
|
operations['createCustomer']['responses']['201']['content']['application/json']
|
||||||
|
|
||||||
const STORAGE_KEY = 'comfy_api_key'
|
const STORAGE_KEY = 'comfy_api_key'
|
||||||
|
|
||||||
export const useApiKeyAuthStore = defineStore('apiKeyAuth', () => {
|
export const useApiKeyAuthStore = defineStore('apiKeyAuth', () => {
|
||||||
|
const firebaseAuthStore = useFirebaseAuthStore()
|
||||||
const apiKey = useLocalStorage<string | null>(STORAGE_KEY, null)
|
const apiKey = useLocalStorage<string | null>(STORAGE_KEY, null)
|
||||||
const toastStore = useToastStore()
|
const toastStore = useToastStore()
|
||||||
const { wrapWithErrorHandlingAsync, toastErrorHandler } = useErrorHandling()
|
const { wrapWithErrorHandlingAsync, toastErrorHandler } = useErrorHandling()
|
||||||
|
|
||||||
|
const currentUser = ref<ComfyApiUser | null>(null)
|
||||||
|
const isAuthenticated = computed(() => !!currentUser.value)
|
||||||
|
|
||||||
|
const initializeUserFromApiKey = async () => {
|
||||||
|
const createCustomerResponse = await firebaseAuthStore.createCustomer()
|
||||||
|
if (!createCustomerResponse) {
|
||||||
|
apiKey.value = null
|
||||||
|
throw new Error(t('auth.login.noAssociatedUser'))
|
||||||
|
}
|
||||||
|
currentUser.value = createCustomerResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
apiKey,
|
||||||
|
() => {
|
||||||
|
if (apiKey.value) {
|
||||||
|
// IF API key is set, initialize user
|
||||||
|
void initializeUserFromApiKey()
|
||||||
|
} else {
|
||||||
|
// IF API key is cleared, clear user
|
||||||
|
currentUser.value = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
const reportError = (error: unknown) => {
|
const reportError = (error: unknown) => {
|
||||||
if (error instanceof Error && error.message === 'STORAGE_FAILED') {
|
if (error instanceof Error && error.message === 'STORAGE_FAILED') {
|
||||||
toastStore.add({
|
toastStore.add({
|
||||||
@@ -49,7 +82,11 @@ export const useApiKeyAuthStore = defineStore('apiKeyAuth', () => {
|
|||||||
|
|
||||||
const getApiKey = () => apiKey.value
|
const getApiKey = () => apiKey.value
|
||||||
|
|
||||||
const getAuthHeader = () => {
|
/**
|
||||||
|
* Retrieves the appropriate authentication header for API requests if an
|
||||||
|
* API key is available, otherwise returns null.
|
||||||
|
*/
|
||||||
|
const getAuthHeader = (): ApiKeyAuthHeader | null => {
|
||||||
const comfyOrgApiKey = getApiKey()
|
const comfyOrgApiKey = getApiKey()
|
||||||
if (comfyOrgApiKey) {
|
if (comfyOrgApiKey) {
|
||||||
return {
|
return {
|
||||||
@@ -60,7 +97,11 @@ export const useApiKeyAuthStore = defineStore('apiKeyAuth', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hasApiKey: computed(() => !!apiKey.value),
|
// State
|
||||||
|
currentUser,
|
||||||
|
isAuthenticated,
|
||||||
|
|
||||||
|
// Actions
|
||||||
storeApiKey,
|
storeApiKey,
|
||||||
clearStoredApiKey,
|
clearStoredApiKey,
|
||||||
getAuthHeader,
|
getAuthHeader,
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ export const useFirebaseAuthStore = defineStore('firebaseAuth', () => {
|
|||||||
* - null if neither authentication method is available
|
* - null if neither authentication method is available
|
||||||
*/
|
*/
|
||||||
const getAuthHeader = async (): Promise<AuthHeader | null> => {
|
const getAuthHeader = async (): Promise<AuthHeader | null> => {
|
||||||
|
// If available, set header with JWT used to identify the user to Firebase service
|
||||||
const token = await getIdToken()
|
const token = await getIdToken()
|
||||||
if (token) {
|
if (token) {
|
||||||
return {
|
return {
|
||||||
@@ -114,8 +115,8 @@ export const useFirebaseAuthStore = defineStore('firebaseAuth', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiKeyStore = useApiKeyAuthStore()
|
// If not authenticated with Firebase, try falling back to API key if available
|
||||||
return apiKeyStore.getAuthHeader()
|
return useApiKeyAuthStore().getAuthHeader()
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchBalance = async (): Promise<GetCustomerBalanceResponse | null> => {
|
const fetchBalance = async (): Promise<GetCustomerBalanceResponse | null> => {
|
||||||
@@ -356,6 +357,7 @@ export const useFirebaseAuthStore = defineStore('firebaseAuth', () => {
|
|||||||
login,
|
login,
|
||||||
register,
|
register,
|
||||||
logout,
|
logout,
|
||||||
|
createCustomer,
|
||||||
getIdToken,
|
getIdToken,
|
||||||
loginWithGoogle,
|
loginWithGoogle,
|
||||||
loginWithGithub,
|
loginWithGithub,
|
||||||
|
|||||||
Reference in New Issue
Block a user