diff --git a/.cursorrules b/.cursorrules index 61be15792..2a61e6f26 100644 --- a/.cursorrules +++ b/.cursorrules @@ -49,4 +49,6 @@ const additionalInstructions = ` 7. Implement proper error handling 8. Follow Vue 3 style guide and naming conventions 9. Use Vite for fast development and building +10. Use vue-i18n in composition API for any string literals. Place new translation +entries in src/locales/en/main.json. `; diff --git a/src/components/dialog/content/SignInContent.vue b/src/components/dialog/content/SignInContent.vue new file mode 100644 index 000000000..d005ffe87 --- /dev/null +++ b/src/components/dialog/content/SignInContent.vue @@ -0,0 +1,118 @@ + + + diff --git a/src/components/dialog/content/signin/SignInForm.vue b/src/components/dialog/content/signin/SignInForm.vue new file mode 100644 index 000000000..f6da70946 --- /dev/null +++ b/src/components/dialog/content/signin/SignInForm.vue @@ -0,0 +1,89 @@ + + + diff --git a/src/components/dialog/content/signin/SignUpForm.vue b/src/components/dialog/content/signin/SignUpForm.vue new file mode 100644 index 000000000..5457dc9c9 --- /dev/null +++ b/src/components/dialog/content/signin/SignUpForm.vue @@ -0,0 +1,165 @@ + + + diff --git a/src/locales/en/main.json b/src/locales/en/main.json index 9b3afb816..0d2392810 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -1051,5 +1051,56 @@ "noTemplatesToExport": "No templates to export", "failedToFetchLogs": "Failed to fetch server logs", "migrateToLitegraphReroute": "Reroute nodes will be removed in future versions. Click to migrate to litegraph-native reroute." + }, + "auth": { + "login": { + "title": "Log in to your account", + "newUser": "New here?", + "signUp": "Sign up", + "emailLabel": "Email", + "emailPlaceholder": "Enter your email", + "passwordLabel": "Password", + "passwordPlaceholder": "Enter your password", + "confirmPasswordLabel": "Confirm Password", + "confirmPasswordPlaceholder": "Enter the same password again", + "forgotPassword": "Forgot password?", + "loginButton": "Log in", + "orContinueWith": "Or continue with", + "loginWithGoogle": "Log in with Google", + "loginWithGithub": "Log in with Github", + "termsText": "By clicking \"Next\" or \"Sign Up\", you agree to our", + "termsLink": "Terms of Use", + "andText": "and", + "privacyLink": "Privacy Policy", + "success": "Login successful", + "failed": "Login failed" + }, + "signup": { + "title": "Create an account", + "alreadyHaveAccount": "Already have an account?", + "emailLabel": "Email", + "emailPlaceholder": "Enter your email", + "passwordLabel": "Password", + "passwordPlaceholder": "Enter new password", + "signUpButton": "Sign up", + "signIn": "Sign in", + "signUpWithGoogle": "Sign up with Google", + "signUpWithGithub": "Sign up with Github" + } + }, + "validation": { + "invalidEmail": "Invalid email address", + "required": "Required", + "minLength": "Must be at least {length} characters", + "maxLength": "Must be no more than {length} characters", + "password": { + "requirements": "Password requirements", + "minLength": "Must be between 8 and 32 characters", + "uppercase": "Must contain at least one uppercase letter", + "lowercase": "Must contain at least one lowercase letter", + "number": "Must contain at least one number", + "special": "Must contain at least one special character", + "match": "Passwords must match" + } } } \ No newline at end of file diff --git a/src/locales/es/main.json b/src/locales/es/main.json index 7d380a8cf..5953ab272 100644 --- a/src/locales/es/main.json +++ b/src/locales/es/main.json @@ -8,6 +8,42 @@ "message": "Este flujo de trabajo contiene nodos de API, que requieren que inicies sesión en tu cuenta para poder ejecutar.", "title": "Se requiere iniciar sesión para usar los nodos de API" }, + "auth": { + "login": { + "andText": "y", + "confirmPasswordLabel": "Confirmar contraseña", + "confirmPasswordPlaceholder": "Ingresa la misma contraseña nuevamente", + "emailLabel": "Correo electrónico", + "emailPlaceholder": "Ingresa tu correo electrónico", + "failed": "Inicio de sesión fallido", + "forgotPassword": "¿Olvidaste tu contraseña?", + "loginButton": "Iniciar sesión", + "loginWithGithub": "Iniciar sesión con Github", + "loginWithGoogle": "Iniciar sesión con Google", + "newUser": "¿Eres nuevo aquí?", + "orContinueWith": "O continuar con", + "passwordLabel": "Contraseña", + "passwordPlaceholder": "Ingresa tu contraseña", + "privacyLink": "Política de privacidad", + "signUp": "Regístrate", + "success": "Inicio de sesión exitoso", + "termsLink": "Términos de uso", + "termsText": "Al hacer clic en \"Siguiente\" o \"Registrarse\", aceptas nuestros", + "title": "Inicia sesión en tu cuenta" + }, + "signup": { + "alreadyHaveAccount": "¿Ya tienes una cuenta?", + "emailLabel": "Correo electrónico", + "emailPlaceholder": "Ingresa tu correo electrónico", + "passwordLabel": "Contraseña", + "passwordPlaceholder": "Ingresa una nueva contraseña", + "signIn": "Iniciar sesión", + "signUpButton": "Registrarse", + "signUpWithGithub": "Registrarse con Github", + "signUpWithGoogle": "Registrarse con Google", + "title": "Crea una cuenta" + } + }, "clipboard": { "errorMessage": "Error al copiar al portapapeles", "errorNotSupported": "API del portapapeles no soportada en su navegador", @@ -1043,6 +1079,21 @@ "next": "Siguiente", "selectUser": "Selecciona un usuario" }, + "validation": { + "invalidEmail": "Dirección de correo electrónico inválida", + "maxLength": "No debe tener más de {length} caracteres", + "minLength": "Debe tener al menos {length} caracteres", + "password": { + "lowercase": "Debe contener al menos una letra minúscula", + "match": "Las contraseñas deben coincidir", + "minLength": "Debe tener entre 8 y 32 caracteres", + "number": "Debe contener al menos un número", + "requirements": "Requisitos de la contraseña", + "special": "Debe contener al menos un carácter especial", + "uppercase": "Debe contener al menos una letra mayúscula" + }, + "required": "Requerido" + }, "welcome": { "getStarted": "Empezar", "title": "Bienvenido a ComfyUI" diff --git a/src/locales/fr/main.json b/src/locales/fr/main.json index 77a29abe0..710a4ceb2 100644 --- a/src/locales/fr/main.json +++ b/src/locales/fr/main.json @@ -8,6 +8,42 @@ "message": "Ce flux de travail contient des nœuds API, qui nécessitent que vous soyez connecté à votre compte pour pouvoir fonctionner.", "title": "Connexion requise pour utiliser les nœuds API" }, + "auth": { + "login": { + "andText": "et", + "confirmPasswordLabel": "Confirmer le mot de passe", + "confirmPasswordPlaceholder": "Entrez à nouveau le même mot de passe", + "emailLabel": "Email", + "emailPlaceholder": "Entrez votre email", + "failed": "Échec de la connexion", + "forgotPassword": "Mot de passe oublié?", + "loginButton": "Se connecter", + "loginWithGithub": "Se connecter avec Github", + "loginWithGoogle": "Se connecter avec Google", + "newUser": "Nouveau ici?", + "orContinueWith": "Ou continuer avec", + "passwordLabel": "Mot de passe", + "passwordPlaceholder": "Entrez votre mot de passe", + "privacyLink": "Politique de confidentialité", + "signUp": "S'inscrire", + "success": "Connexion réussie", + "termsLink": "Conditions d'utilisation", + "termsText": "En cliquant sur \"Suivant\" ou \"S'inscrire\", vous acceptez nos", + "title": "Connectez-vous à votre compte" + }, + "signup": { + "alreadyHaveAccount": "Vous avez déjà un compte?", + "emailLabel": "Email", + "emailPlaceholder": "Entrez votre email", + "passwordLabel": "Mot de passe", + "passwordPlaceholder": "Entrez un nouveau mot de passe", + "signIn": "Se connecter", + "signUpButton": "S'inscrire", + "signUpWithGithub": "S'inscrire avec Github", + "signUpWithGoogle": "S'inscrire avec Google", + "title": "Créer un compte" + } + }, "clipboard": { "errorMessage": "Échec de la copie dans le presse-papiers", "errorNotSupported": "L'API du presse-papiers n'est pas prise en charge par votre navigateur", @@ -1043,6 +1079,21 @@ "next": "Suivant", "selectUser": "Sélectionnez un utilisateur" }, + "validation": { + "invalidEmail": "Adresse e-mail invalide", + "maxLength": "Ne doit pas dépasser {length} caractères", + "minLength": "Doit contenir au moins {length} caractères", + "password": { + "lowercase": "Doit contenir au moins une lettre minuscule", + "match": "Les mots de passe doivent correspondre", + "minLength": "Doit contenir entre 8 et 32 caractères", + "number": "Doit contenir au moins un chiffre", + "requirements": "Exigences du mot de passe", + "special": "Doit contenir au moins un caractère spécial", + "uppercase": "Doit contenir au moins une lettre majuscule" + }, + "required": "Requis" + }, "welcome": { "getStarted": "Commencer", "title": "Bienvenue sur ComfyUI" diff --git a/src/locales/ja/main.json b/src/locales/ja/main.json index 7aca6d48c..57661dd10 100644 --- a/src/locales/ja/main.json +++ b/src/locales/ja/main.json @@ -8,6 +8,42 @@ "message": "このワークフローにはAPIノードが含まれており、実行するためにはアカウントにサインインする必要があります。", "title": "APIノードを使用するためにはサインインが必要です" }, + "auth": { + "login": { + "andText": "および", + "confirmPasswordLabel": "パスワードの確認", + "confirmPasswordPlaceholder": "もう一度同じパスワードを入力してください", + "emailLabel": "メール", + "emailPlaceholder": "メールアドレスを入力してください", + "failed": "ログイン失敗", + "forgotPassword": "パスワードを忘れましたか?", + "loginButton": "ログイン", + "loginWithGithub": "Githubでログイン", + "loginWithGoogle": "Googleでログイン", + "newUser": "新規ユーザーですか?", + "orContinueWith": "または以下で続ける", + "passwordLabel": "パスワード", + "passwordPlaceholder": "パスワードを入力してください", + "privacyLink": "プライバシーポリシー", + "signUp": "サインアップ", + "success": "ログイン成功", + "termsLink": "利用規約", + "termsText": "「次へ」または「サインアップ」をクリックすると、私たちの", + "title": "アカウントにログインする" + }, + "signup": { + "alreadyHaveAccount": "すでにアカウントをお持ちですか?", + "emailLabel": "メール", + "emailPlaceholder": "メールアドレスを入力してください", + "passwordLabel": "パスワード", + "passwordPlaceholder": "新しいパスワードを入力してください", + "signIn": "サインイン", + "signUpButton": "サインアップ", + "signUpWithGithub": "Githubでサインアップ", + "signUpWithGoogle": "Googleでサインアップ", + "title": "アカウントを作成する" + } + }, "clipboard": { "errorMessage": "クリップボードへのコピーに失敗しました", "errorNotSupported": "お使いのブラウザではクリップボードAPIがサポートされていません", @@ -1043,6 +1079,21 @@ "next": "次へ", "selectUser": "ユーザーを選択" }, + "validation": { + "invalidEmail": "無効なメールアドレス", + "maxLength": "{length}文字以下でなければなりません", + "minLength": "{length}文字以上でなければなりません", + "password": { + "lowercase": "少なくとも1つの小文字を含む必要があります", + "match": "パスワードが一致する必要があります", + "minLength": "8文字から32文字の間でなければなりません", + "number": "少なくとも1つの数字を含む必要があります", + "requirements": "パスワードの要件", + "special": "少なくとも1つの特殊文字を含む必要があります", + "uppercase": "少なくとも1つの大文字を含む必要があります" + }, + "required": "必須" + }, "welcome": { "getStarted": "はじめる", "title": "ComfyUIへようこそ" diff --git a/src/locales/ko/main.json b/src/locales/ko/main.json index a5c55389d..2d6197d24 100644 --- a/src/locales/ko/main.json +++ b/src/locales/ko/main.json @@ -8,6 +8,42 @@ "message": "이 워크플로우에는 API 노드가 포함되어 있으며, 실행하려면 계정에 로그인해야 합니다.", "title": "API 노드 사용에 필요한 로그인" }, + "auth": { + "login": { + "andText": "및", + "confirmPasswordLabel": "비밀번호 확인", + "confirmPasswordPlaceholder": "동일한 비밀번호를 다시 입력하세요", + "emailLabel": "이메일", + "emailPlaceholder": "이메일을 입력하세요", + "failed": "로그인 실패", + "forgotPassword": "비밀번호를 잊으셨나요?", + "loginButton": "로그인", + "loginWithGithub": "Github로 로그인", + "loginWithGoogle": "구글로 로그인", + "newUser": "처음이신가요?", + "orContinueWith": "또는 다음으로 계속", + "passwordLabel": "비밀번호", + "passwordPlaceholder": "비밀번호를 입력하세요", + "privacyLink": "개인정보 보호정책", + "signUp": "가입하기", + "success": "로그인 성공", + "termsLink": "이용 약관", + "termsText": "\"다음\" 또는 \"가입하기\"를 클릭하면 우리의", + "title": "계정에 로그인" + }, + "signup": { + "alreadyHaveAccount": "이미 계정이 있으신가요?", + "emailLabel": "이메일", + "emailPlaceholder": "이메일을 입력하세요", + "passwordLabel": "비밀번호", + "passwordPlaceholder": "새 비밀번호를 입력하세요", + "signIn": "로그인", + "signUpButton": "가입하기", + "signUpWithGithub": "Github로 가입하기", + "signUpWithGoogle": "구글로 가입하기", + "title": "계정 생성" + } + }, "clipboard": { "errorMessage": "클립보드에 복사하지 못했습니다", "errorNotSupported": "브라우저가 클립보드 API를 지원하지 않습니다.", @@ -1043,6 +1079,21 @@ "next": "다음", "selectUser": "사용자 선택" }, + "validation": { + "invalidEmail": "유효하지 않은 이메일 주소", + "maxLength": "{length}자를 초과할 수 없습니다", + "minLength": "{length}자 이상이어야 합니다", + "password": { + "lowercase": "적어도 하나의 소문자를 포함해야 합니다", + "match": "비밀번호가 일치해야 합니다", + "minLength": "8자에서 32자 사이여야 합니다", + "number": "적어도 하나의 숫자를 포함해야 합니다", + "requirements": "비밀번호 요구사항", + "special": "적어도 하나의 특수 문자를 포함해야 합니다", + "uppercase": "적어도 하나의 대문자를 포함해야 합니다" + }, + "required": "필수" + }, "welcome": { "getStarted": "시작하기", "title": "ComfyUI에 오신 것을 환영합니다" diff --git a/src/locales/ru/main.json b/src/locales/ru/main.json index 1c150d8da..f0ab295ed 100644 --- a/src/locales/ru/main.json +++ b/src/locales/ru/main.json @@ -8,6 +8,42 @@ "message": "Этот рабочий процесс содержит API Nodes, которые требуют входа в вашу учетную запись для выполнения.", "title": "Требуется вход для использования API Nodes" }, + "auth": { + "login": { + "andText": "и", + "confirmPasswordLabel": "Подтвердите пароль", + "confirmPasswordPlaceholder": "Введите тот же пароль еще раз", + "emailLabel": "Электронная почта", + "emailPlaceholder": "Введите вашу электронную почту", + "failed": "Вход не удался", + "forgotPassword": "Забыли пароль?", + "loginButton": "Войти", + "loginWithGithub": "Войти через Github", + "loginWithGoogle": "Войти через Google", + "newUser": "Вы здесь впервые?", + "orContinueWith": "Или продолжить с", + "passwordLabel": "Пароль", + "passwordPlaceholder": "Введите ваш пароль", + "privacyLink": "Политикой конфиденциальности", + "signUp": "Зарегистрироваться", + "success": "Вход выполнен успешно", + "termsLink": "Условиями использования", + "termsText": "Нажимая \"Далее\" или \"Зарегистрироваться\", вы соглашаетесь с нашими", + "title": "Войдите в свой аккаунт" + }, + "signup": { + "alreadyHaveAccount": "Уже есть аккаунт?", + "emailLabel": "Электронная почта", + "emailPlaceholder": "Введите вашу электронную почту", + "passwordLabel": "Пароль", + "passwordPlaceholder": "Введите новый пароль", + "signIn": "Войти", + "signUpButton": "Зарегистрироваться", + "signUpWithGithub": "Зарегистрироваться через Github", + "signUpWithGoogle": "Зарегистрироваться через Google", + "title": "Создать аккаунт" + } + }, "clipboard": { "errorMessage": "Не удалось скопировать в буфер обмена", "errorNotSupported": "API буфера обмена не поддерживается в вашем браузере", @@ -1043,6 +1079,21 @@ "next": "Далее", "selectUser": "Выберите пользователя" }, + "validation": { + "invalidEmail": "Недействительный адрес электронной почты", + "maxLength": "Должно быть не более {length} символов", + "minLength": "Должно быть не менее {length} символов", + "password": { + "lowercase": "Должен содержать хотя бы одну строчную букву", + "match": "Пароли должны совпадать", + "minLength": "Должно быть от 8 до 32 символов", + "number": "Должен содержать хотя бы одну цифру", + "requirements": "Требования к паролю", + "special": "Должен содержать хотя бы один специальный символ", + "uppercase": "Должен содержать хотя бы одну заглавную букву" + }, + "required": "Обязательно" + }, "welcome": { "getStarted": "Начать", "title": "Добро пожаловать в ComfyUI" diff --git a/src/locales/zh/main.json b/src/locales/zh/main.json index 78eb185ae..6d4d433e3 100644 --- a/src/locales/zh/main.json +++ b/src/locales/zh/main.json @@ -8,6 +8,42 @@ "message": "此工作流包含API节点,需要您登录账户才能运行。", "title": "使用API节点需要登录" }, + "auth": { + "login": { + "andText": "和", + "confirmPasswordLabel": "确认密码", + "confirmPasswordPlaceholder": "再次输入相同的密码", + "emailLabel": "电子邮件", + "emailPlaceholder": "输入您的电子邮件", + "failed": "登录失败", + "forgotPassword": "忘记密码?", + "loginButton": "登录", + "loginWithGithub": "使用Github登录", + "loginWithGoogle": "使用Google登录", + "newUser": "新来的?", + "orContinueWith": "或者继续使用", + "passwordLabel": "密码", + "passwordPlaceholder": "输入您的密码", + "privacyLink": "隐私政策", + "signUp": "注册", + "success": "登录成功", + "termsLink": "使用条款", + "termsText": "点击“下一步”或“注册”即表示您同意我们的", + "title": "登录您的账户" + }, + "signup": { + "alreadyHaveAccount": "已经有账户了?", + "emailLabel": "电子邮件", + "emailPlaceholder": "输入您的电子邮件", + "passwordLabel": "密码", + "passwordPlaceholder": "输入新密码", + "signIn": "登录", + "signUpButton": "注册", + "signUpWithGithub": "使用Github注册", + "signUpWithGoogle": "使用Google注册", + "title": "创建一个账户" + } + }, "clipboard": { "errorMessage": "复制到剪贴板失败", "errorNotSupported": "您的浏览器不支持剪贴板API", @@ -1043,6 +1079,21 @@ "next": "下一步", "selectUser": "选择用户" }, + "validation": { + "invalidEmail": "无效的电子邮件地址", + "maxLength": "不能超过{length}个字符", + "minLength": "必须至少有{length}个字符", + "password": { + "lowercase": "必须包含至少一个小写字母", + "match": "密码必须匹配", + "minLength": "必须在8到32个字符之间", + "number": "必须包含至少一个数字", + "requirements": "密码要求", + "special": "必须包含至少一个特殊字符", + "uppercase": "必须包含至少一个大写字母" + }, + "required": "必填" + }, "welcome": { "getStarted": "开始使用", "title": "欢迎使用 ComfyUI" diff --git a/src/schemas/signInSchema.ts b/src/schemas/signInSchema.ts new file mode 100644 index 000000000..c70dc1cda --- /dev/null +++ b/src/schemas/signInSchema.ts @@ -0,0 +1,36 @@ +import { z } from 'zod' + +import { t } from '@/i18n' + +export const signInSchema = z.object({ + email: z + .string() + .email(t('validation.invalidEmail')) + .min(1, t('validation.required')), + password: z.string().min(1, t('validation.required')) +}) + +export type SignInData = z.infer + +export const signUpSchema = z + .object({ + email: z + .string() + .email(t('validation.invalidEmail')) + .min(1, t('validation.required')), + password: z + .string() + .min(8, t('validation.minLength', { length: 8 })) + .max(32, t('validation.maxLength', { length: 32 })) + .regex(/[A-Z]/, t('validation.password.uppercase')) + .regex(/[a-z]/, t('validation.password.lowercase')) + .regex(/\d/, t('validation.password.number')) + .regex(/[^A-Za-z0-9]/, t('validation.password.special')), + confirmPassword: z.string().min(1, t('validation.required')) + }) + .refine((data) => data.password === data.confirmPassword, { + message: t('validation.password.match'), + path: ['confirmPassword'] + }) + +export type SignUpData = z.infer diff --git a/src/services/dialogService.ts b/src/services/dialogService.ts index a47e6748c..acd6bb869 100644 --- a/src/services/dialogService.ts +++ b/src/services/dialogService.ts @@ -7,6 +7,7 @@ import ManagerProgressDialogContent from '@/components/dialog/content/ManagerPro import MissingModelsWarning from '@/components/dialog/content/MissingModelsWarning.vue' import PromptDialogContent from '@/components/dialog/content/PromptDialogContent.vue' import SettingDialogContent from '@/components/dialog/content/SettingDialogContent.vue' +import SignInContent from '@/components/dialog/content/SignInContent.vue' import ManagerDialogContent from '@/components/dialog/content/manager/ManagerDialogContent.vue' import ManagerHeader from '@/components/dialog/content/manager/ManagerHeader.vue' import ManagerProgressFooter from '@/components/dialog/footer/ManagerProgressFooter.vue' @@ -232,7 +233,7 @@ export const useDialogService = () => { component: ApiNodesSignInContent, props: { apiNodes, - onLogin: () => resolve(true), + onLogin: () => showSignInDialog().then((result) => resolve(result)), onCancel: () => resolve(false) }, headerComponent: ComfyOrgHeader, @@ -247,6 +248,26 @@ export const useDialogService = () => { }) } + async function showSignInDialog(): Promise { + return new Promise((resolve) => { + dialogStore.showDialog({ + key: 'global-signin', + component: SignInContent, + headerComponent: ComfyOrgHeader, + props: { + onSuccess: () => resolve(true) + }, + dialogComponentProps: { + closable: false, + onClose: () => resolve(false) + } + }) + }).then((result) => { + dialogStore.closeDialog({ key: 'global-signin' }) + return result + }) + } + async function prompt({ title, message, @@ -332,6 +353,7 @@ export const useDialogService = () => { showManagerProgressDialog, showErrorDialog, showApiNodesSignInDialog, + showSignInDialog, prompt, confirm }