Описание изменений
This commit is contained in:
@@ -1,195 +1,117 @@
|
||||
<template>
|
||||
<div class="email-connect">
|
||||
<p>Подключите свой email для быстрой авторизации.</p>
|
||||
|
||||
<div class="email-form">
|
||||
<div class="email-connection">
|
||||
<div v-if="!showVerification">
|
||||
<input
|
||||
type="email"
|
||||
v-model="email"
|
||||
placeholder="Введите ваш email"
|
||||
:disabled="loading || verificationSent"
|
||||
v-model="email"
|
||||
type="email"
|
||||
placeholder="Введите email"
|
||||
class="email-input"
|
||||
/>
|
||||
<button
|
||||
@click="sendVerification"
|
||||
class="connect-button"
|
||||
:disabled="!isValidEmail || loading || verificationSent"
|
||||
@click="requestCode"
|
||||
:disabled="isLoading || !isValidEmail"
|
||||
class="email-btn"
|
||||
>
|
||||
<span class="email-icon">✉️</span> {{ verificationSent ? 'Код отправлен' : 'Отправить код' }}
|
||||
Получить код
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="verificationSent" class="verification-form">
|
||||
<div v-else>
|
||||
<input
|
||||
type="text"
|
||||
v-model="verificationCode"
|
||||
placeholder="Введите код подтверждения"
|
||||
:disabled="loading"
|
||||
v-model="code"
|
||||
type="text"
|
||||
placeholder="Введите код"
|
||||
class="code-input"
|
||||
/>
|
||||
<button
|
||||
@click="verifyEmail"
|
||||
class="verify-button"
|
||||
:disabled="!verificationCode || loading"
|
||||
@click="verifyCode"
|
||||
:disabled="isLoading"
|
||||
class="verify-btn"
|
||||
>
|
||||
Подтвердить
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="loading" class="loading">Загрузка...</div>
|
||||
<div v-if="error" class="error">{{ error }}</div>
|
||||
<div v-if="success" class="success">{{ success }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import axios from 'axios';
|
||||
|
||||
const email = ref('');
|
||||
const verificationCode = ref('');
|
||||
const loading = ref(false);
|
||||
const error = ref('');
|
||||
const success = ref('');
|
||||
const verificationSent = ref(false);
|
||||
|
||||
const isValidEmail = computed(() => {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailRegex.test(email.value);
|
||||
const props = defineProps({
|
||||
onEmailAuth: {
|
||||
type: Function,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
async function sendVerification() {
|
||||
if (!isValidEmail.value) return;
|
||||
|
||||
try {
|
||||
loading.value = true;
|
||||
error.value = '';
|
||||
success.value = '';
|
||||
|
||||
// Запрос на отправку кода подтверждения
|
||||
const response = await axios.post('/api/auth/email', {
|
||||
email: email.value
|
||||
}, {
|
||||
withCredentials: true
|
||||
});
|
||||
|
||||
if (response.data.error) {
|
||||
error.value = `Ошибка: ${response.data.error}`;
|
||||
return;
|
||||
}
|
||||
|
||||
verificationSent.value = true;
|
||||
success.value = `Код подтверждения отправлен на ${email.value}`;
|
||||
} catch (err) {
|
||||
console.error('Error sending verification code:', err);
|
||||
error.value = 'Ошибка при отправке кода подтверждения';
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
const email = ref('');
|
||||
const code = ref('');
|
||||
const error = ref('');
|
||||
const isLoading = ref(false);
|
||||
const showVerification = ref(false);
|
||||
|
||||
async function verifyEmail() {
|
||||
if (!verificationCode.value) return;
|
||||
|
||||
const isValidEmail = computed(() => {
|
||||
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value);
|
||||
});
|
||||
|
||||
const requestCode = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
isLoading.value = true;
|
||||
await props.onEmailAuth(email.value);
|
||||
showVerification.value = true;
|
||||
error.value = '';
|
||||
success.value = '';
|
||||
|
||||
// Запрос на проверку кода
|
||||
const response = await axios.post('/api/auth/email/verify', {
|
||||
email: email.value,
|
||||
code: verificationCode.value
|
||||
}, {
|
||||
withCredentials: true
|
||||
});
|
||||
|
||||
if (response.data.error) {
|
||||
error.value = `Ошибка: ${response.data.error}`;
|
||||
return;
|
||||
}
|
||||
|
||||
success.value = 'Email успешно подтвержден';
|
||||
|
||||
// Сбрасываем форму
|
||||
setTimeout(() => {
|
||||
email.value = '';
|
||||
verificationCode.value = '';
|
||||
verificationSent.value = false;
|
||||
success.value = '';
|
||||
}, 3000);
|
||||
} catch (err) {
|
||||
console.error('Error verifying email:', err);
|
||||
error.value = 'Ошибка при проверке кода подтверждения';
|
||||
error.value = err.message || 'Ошибка отправки кода';
|
||||
} finally {
|
||||
loading.value = false;
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const verifyCode = async () => {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
await props.onEmailAuth(email.value, code.value);
|
||||
error.value = '';
|
||||
} catch (err) {
|
||||
error.value = err.message || 'Неверный код';
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.email-connect {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
.email-connection {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.email-form, .verification-form {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
input {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
.email-input,
|
||||
.code-input {
|
||||
padding: 8px;
|
||||
margin-right: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.connect-button, .verify-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10px 15px;
|
||||
background-color: #4caf50;
|
||||
.email-btn,
|
||||
.verify-btn {
|
||||
padding: 10px 20px;
|
||||
background-color: #48bb78;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: background-color 0.2s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.connect-button:hover, .verify-button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
|
||||
.connect-button:disabled, .verify-button:disabled {
|
||||
background-color: #cccccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.email-icon {
|
||||
margin-right: 10px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.loading, .error, .success {
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.loading {
|
||||
background-color: #f8f9fa;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.error {
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
color: #e53e3e;
|
||||
margin-top: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.success {
|
||||
background-color: #d4edda;
|
||||
color: #155724;
|
||||
button:disabled {
|
||||
opacity: 0.7;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useAuthStore } from '../../stores/auth';
|
||||
|
||||
const authStore = useAuthStore();
|
||||
const identities = ref({});
|
||||
const newIdentity = ref({ type: 'email', value: '' });
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const response = await fetch('/api/access/tokens', {
|
||||
credentials: 'include'
|
||||
});
|
||||
const data = await response.json();
|
||||
identities.value = data.identities || {};
|
||||
} catch (err) {
|
||||
error.value = 'Ошибка при загрузке идентификаторов';
|
||||
console.error(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
async function addIdentity() {
|
||||
try {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
|
||||
const success = await authStore.linkIdentity(
|
||||
newIdentity.value.type,
|
||||
newIdentity.value.value
|
||||
);
|
||||
|
||||
if (success) {
|
||||
identities.value = authStore.identities;
|
||||
newIdentity.value.value = '';
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = 'Ошибка при добавлении идентификатора';
|
||||
console.error(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
84
frontend/src/components/identity/WalletConnection.vue
Normal file
84
frontend/src/components/identity/WalletConnection.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div class="wallet-connection">
|
||||
<button
|
||||
@click="connectWallet"
|
||||
:disabled="isLoading"
|
||||
class="wallet-btn"
|
||||
>
|
||||
{{ isAuthenticated ? 'Подключено' : 'Подключить кошелек' }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { connectWithWallet } from '../../services/wallet';
|
||||
|
||||
// Определяем props
|
||||
const props = defineProps({
|
||||
isAuthenticated: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
// Определяем состояние
|
||||
const isLoading = ref(false);
|
||||
|
||||
const emit = defineEmits(['connect']);
|
||||
|
||||
// Метод подключения кошелька
|
||||
const connectWallet = async () => {
|
||||
if (isLoading.value) return;
|
||||
|
||||
try {
|
||||
isLoading.value = true;
|
||||
// Получаем адрес кошелька
|
||||
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
|
||||
const address = accounts[0];
|
||||
|
||||
// Получаем nonce
|
||||
const nonceResponse = await api.get(`/api/auth/nonce?address=${address}`);
|
||||
const nonce = nonceResponse.data.nonce;
|
||||
|
||||
// Подписываем сообщение
|
||||
const message = `${window.location.host} wants you to sign in with your Ethereum account:\n${address.slice(0, 42)}...`;
|
||||
const signature = await window.ethereum.request({
|
||||
method: 'personal_sign',
|
||||
params: [message, address]
|
||||
});
|
||||
|
||||
emit('connect', { address, signature, message });
|
||||
} catch (error) {
|
||||
console.error('Error connecting wallet:', error);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.wallet-connection {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.wallet-btn {
|
||||
padding: 10px 20px;
|
||||
background-color: #4a5568;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.wallet-btn:hover:not(:disabled) {
|
||||
background-color: #2d3748;
|
||||
}
|
||||
|
||||
.wallet-btn:disabled {
|
||||
opacity: 0.7;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
9
frontend/src/components/identity/index.js
Normal file
9
frontend/src/components/identity/index.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import TelegramConnect from './TelegramConnect.vue';
|
||||
import WalletConnection from './WalletConnection.vue';
|
||||
import EmailConnect from './EmailConnect.vue';
|
||||
|
||||
export {
|
||||
TelegramConnect,
|
||||
WalletConnection,
|
||||
EmailConnect
|
||||
};
|
||||
Reference in New Issue
Block a user