Описание изменений
This commit is contained in:
195
frontend/src/components/TelegramConnect.vue
Normal file
195
frontend/src/components/TelegramConnect.vue
Normal file
@@ -0,0 +1,195 @@
|
||||
<template>
|
||||
<div class="telegram-auth">
|
||||
<div v-if="!isAuthenticating">
|
||||
<a :href="telegramBotLink" target="_blank" class="telegram-btn" @click="startAuth">
|
||||
<span class="auth-icon">📱</span> Подключить Telegram
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div v-else class="auth-progress">
|
||||
<p>Для завершения авторизации:</p>
|
||||
<ol>
|
||||
<li>Перейдите в Telegram-бота <strong>@{{ botUsername }}</strong></li>
|
||||
<li>Если бот не открылся автоматически, скопируйте и отправьте ему команду:</li>
|
||||
</ol>
|
||||
|
||||
<div class="auth-code">
|
||||
/auth {{ authToken }}
|
||||
</div>
|
||||
<button class="copy-btn" @click="copyAuthCommand">Копировать команду</button>
|
||||
|
||||
<div class="auth-actions">
|
||||
<button class="cancel-btn" @click="cancelAuth">Отмена</button>
|
||||
<button class="check-btn" @click="checkAuthStatus">Проверить статус</button>
|
||||
</div>
|
||||
|
||||
<div v-if="errorMessage" class="error-message">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { useAuthStore } from '../stores/auth';
|
||||
|
||||
const auth = useAuthStore();
|
||||
|
||||
const isAuthenticating = ref(false);
|
||||
const authToken = ref('');
|
||||
const botUsername = ref(process.env.VUE_APP_TELEGRAM_BOT_USERNAME || 'HB3_Accelerator_Bot');
|
||||
const errorMessage = ref('');
|
||||
const checkInterval = ref(null);
|
||||
|
||||
// Формируем ссылку на бота с параметром авторизации
|
||||
const telegramBotLink = computed(() => {
|
||||
// Возвращаем ссылку только если есть токен
|
||||
if (!authToken.value) return `https://t.me/${botUsername.value}`;
|
||||
return `https://t.me/${botUsername.value}?start=auth_${authToken.value}`;
|
||||
});
|
||||
|
||||
async function startAuth() {
|
||||
try {
|
||||
// Сначала запрашиваем токен
|
||||
const response = await auth.createTelegramAuthToken();
|
||||
|
||||
if (response.success) {
|
||||
authToken.value = response.token;
|
||||
|
||||
// Теперь можно включить режим авторизации
|
||||
isAuthenticating.value = true;
|
||||
|
||||
// И запустить проверку
|
||||
checkInterval.value = setInterval(checkAuthStatus, 3000);
|
||||
|
||||
// Открываем Telegram
|
||||
console.log(`Открывается ссылка на Telegram: ${telegramBotLink.value}`);
|
||||
window.open(telegramBotLink.value, '_blank');
|
||||
} else {
|
||||
errorMessage.value = response.error || 'Не удалось начать авторизацию';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error starting Telegram auth:', error);
|
||||
errorMessage.value = 'Ошибка при инициализации авторизации';
|
||||
}
|
||||
}
|
||||
|
||||
async function checkAuthStatus() {
|
||||
try {
|
||||
const response = await auth.checkTelegramAuthStatus(authToken.value);
|
||||
|
||||
if (response.success && response.authenticated) {
|
||||
// Авторизация успешна, очищаем интервал и состояние
|
||||
clearInterval(checkInterval.value);
|
||||
isAuthenticating.value = false;
|
||||
|
||||
// Здесь можно добавить дополнительные действия после успешной авторизации
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking auth status:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function cancelAuth() {
|
||||
clearInterval(checkInterval.value);
|
||||
isAuthenticating.value = false;
|
||||
authToken.value = '';
|
||||
errorMessage.value = '';
|
||||
}
|
||||
|
||||
function copyAuthCommand() {
|
||||
const command = `/auth ${authToken.value}`;
|
||||
navigator.clipboard.writeText(command);
|
||||
// Можно добавить уведомление о копировании
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.telegram-auth {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.telegram-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #0088cc;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.auth-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.auth-progress {
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.auth-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.check-btn {
|
||||
background-color: #0088cc;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: #ff4d4f;
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.auth-code {
|
||||
font-family: monospace;
|
||||
font-size: 16px;
|
||||
padding: 12px;
|
||||
background-color: #f1f1f1;
|
||||
border-radius: 4px;
|
||||
margin: 15px 0;
|
||||
white-space: nowrap;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.copy-btn:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
</style>
|
||||
@@ -260,6 +260,167 @@ export const useAuthStore = defineStore('auth', {
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
async requestEmailVerification(email) {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await axios.post('/api/auth/email/request', { email });
|
||||
|
||||
console.log('Email verification code response:', response.data);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: response.data.message,
|
||||
verificationCode: response.data.verificationCode // Для разработки
|
||||
};
|
||||
} catch (error) {
|
||||
this.error = error.response?.data?.error || 'Ошибка запроса кода';
|
||||
return { success: false, error: this.error };
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async verifyEmail(code) {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await axios.post('/api/auth/email/verify', { code });
|
||||
|
||||
if (response.data.success) {
|
||||
this.isAuthenticated = true;
|
||||
this.user = {
|
||||
id: response.data.userId,
|
||||
email: response.data.email
|
||||
};
|
||||
|
||||
if (response.data.walletAddress) {
|
||||
this.user.address = response.data.walletAddress;
|
||||
this.address = response.data.walletAddress;
|
||||
}
|
||||
|
||||
this.isAdmin = response.data.isAdmin;
|
||||
this.authType = 'email';
|
||||
}
|
||||
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
this.error = error.response?.data?.error || 'Ошибка верификации';
|
||||
return { success: false, error: this.error };
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async requestTelegramCode() {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await axios.get('/api/auth/telegram/code');
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
this.error = error.response?.data?.error || 'Ошибка запроса кода';
|
||||
return { success: false, error: this.error };
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async verifyTelegram(telegramId, code) {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await axios.post('/api/auth/telegram/verify', { telegramId, code });
|
||||
|
||||
if (response.data.success) {
|
||||
this.isAuthenticated = true;
|
||||
this.user = {
|
||||
id: response.data.userId,
|
||||
telegramId: response.data.telegramId
|
||||
};
|
||||
|
||||
if (response.data.walletAddress) {
|
||||
this.user.address = response.data.walletAddress;
|
||||
this.address = response.data.walletAddress;
|
||||
}
|
||||
|
||||
this.isAdmin = response.data.isAdmin;
|
||||
this.authType = 'telegram';
|
||||
}
|
||||
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
this.error = error.response?.data?.error || 'Ошибка верификации';
|
||||
return { success: false, error: this.error };
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async linkIdentity(type, value) {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await axios.post('/api/auth/link-identity', { type, value });
|
||||
|
||||
if (response.data.success) {
|
||||
if (type === 'wallet') {
|
||||
this.user.address = value;
|
||||
this.address = value;
|
||||
} else if (type === 'email') {
|
||||
this.user.email = value;
|
||||
} else if (type === 'telegram') {
|
||||
this.user.telegramId = value;
|
||||
}
|
||||
|
||||
this.isAdmin = response.data.isAdmin;
|
||||
}
|
||||
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
this.error = error.response?.data?.error || 'Ошибка связывания аккаунта';
|
||||
return { success: false, error: this.error };
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async createTelegramAuthToken() {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await axios.post('/api/auth/telegram/auth-token');
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
this.error = error.response?.data?.error || 'Ошибка создания токена';
|
||||
return { success: false, error: this.error };
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async checkTelegramAuthStatus(token) {
|
||||
try {
|
||||
const response = await axios.get(`/api/auth/telegram/auth-status/${token}`);
|
||||
|
||||
if (response.data.success && response.data.authenticated) {
|
||||
// Обновляем состояние аутентификации
|
||||
await this.checkAuth();
|
||||
}
|
||||
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error checking Telegram auth status:', error);
|
||||
return { success: false, error: 'Ошибка проверки статуса' };
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -30,22 +30,42 @@
|
||||
</div>
|
||||
|
||||
<div class="auth-option">
|
||||
<button class="auth-btn telegram-btn" @click="connectTelegram">
|
||||
<span class="auth-icon">📱</span> Подключить Telegram
|
||||
</button>
|
||||
<TelegramConnect />
|
||||
</div>
|
||||
|
||||
<div class="auth-option email-option">
|
||||
<!-- Email аутентификация: первый шаг - запрос кода -->
|
||||
<div v-if="!showEmailVerification" class="auth-option email-option">
|
||||
<input
|
||||
type="email"
|
||||
v-model="email"
|
||||
placeholder="Введите ваш email"
|
||||
class="email-input"
|
||||
/>
|
||||
<button class="auth-btn email-btn" @click="connectEmail" :disabled="!isValidEmail">
|
||||
<button class="auth-btn email-btn" @click="requestEmailCode" :disabled="!isValidEmail">
|
||||
<span class="auth-icon">✉️</span> Подключить Email
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Email аутентификация: второй шаг - ввод кода -->
|
||||
<div v-else class="auth-option email-verification">
|
||||
<p>Код подтверждения отправлен на {{ email }}</p>
|
||||
<input
|
||||
type="text"
|
||||
v-model="emailVerificationCode"
|
||||
placeholder="Введите код подтверждения"
|
||||
class="verification-input"
|
||||
/>
|
||||
<div class="email-verification-actions">
|
||||
<button class="auth-btn email-btn" @click="verifyEmailCode">
|
||||
<span class="auth-icon">✓</span> Подтвердить
|
||||
</button>
|
||||
<button class="auth-btn cancel-btn" @click="cancelEmailVerification">
|
||||
Отмена
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="emailErrorMessage" class="error-message">{{ emailErrorMessage }}</div>
|
||||
</div>
|
||||
|
||||
<div class="message-time">
|
||||
@@ -73,6 +93,7 @@
|
||||
import { ref, computed, onMounted, watch, nextTick } from 'vue';
|
||||
import { useAuthStore } from '../stores/auth';
|
||||
import WalletConnection from '../components/WalletConnection.vue';
|
||||
import TelegramConnect from '../components/TelegramConnect.vue';
|
||||
import axios from '../api/axios';
|
||||
|
||||
console.log('HomeView.vue: Version with chat loaded');
|
||||
@@ -89,6 +110,11 @@ const hasShownAuthMessage = ref(false);
|
||||
const guestMessages = ref([]);
|
||||
const hasShownAuthOptions = ref(false);
|
||||
|
||||
// Email аутентификация
|
||||
const emailVerificationCode = ref('');
|
||||
const showEmailVerification = ref(false);
|
||||
const emailErrorMessage = ref('');
|
||||
|
||||
// Простая функция для выхода
|
||||
const logout = async () => {
|
||||
await auth.logout();
|
||||
@@ -320,95 +346,66 @@ async function connectTelegram() {
|
||||
}
|
||||
}
|
||||
|
||||
// Функция для подключения через Email
|
||||
async function connectEmail() {
|
||||
if (!isValidEmail.value) return;
|
||||
// Запрос кода подтверждения по email
|
||||
async function requestEmailCode() {
|
||||
emailErrorMessage.value = '';
|
||||
|
||||
try {
|
||||
messages.value.push({
|
||||
sender: 'ai',
|
||||
text: `Отправляем код подтверждения на ${email.value}...`,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
const response = await auth.requestEmailVerification(email.value);
|
||||
|
||||
// Отправляем запрос на отправку кода подтверждения
|
||||
const response = await axios.post('/api/auth/email', {
|
||||
email: email.value
|
||||
}, {
|
||||
withCredentials: true
|
||||
});
|
||||
|
||||
if (response.data.error) {
|
||||
messages.value.push({
|
||||
sender: 'ai',
|
||||
text: `Ошибка: ${response.data.error}`,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
messages.value.push({
|
||||
sender: 'ai',
|
||||
text: `На ваш email ${email.value} отправлено письмо с кодом подтверждения. Пожалуйста, введите код:`,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
|
||||
// Добавляем поле для ввода кода
|
||||
const verificationCode = prompt('Введите код подтверждения:');
|
||||
|
||||
if (verificationCode) {
|
||||
try {
|
||||
// Отправляем запрос на проверку кода
|
||||
const verifyResponse = await axios.post('/api/auth/email/verify', {
|
||||
email: email.value,
|
||||
code: verificationCode
|
||||
}, {
|
||||
withCredentials: true
|
||||
});
|
||||
|
||||
if (verifyResponse.data.error) {
|
||||
messages.value.push({
|
||||
sender: 'ai',
|
||||
text: `Ошибка: ${verifyResponse.data.error}`,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
messages.value.push({
|
||||
sender: 'ai',
|
||||
text: 'Email успешно подтвержден! Теперь вы можете использовать все функции чата.',
|
||||
timestamp: new Date(),
|
||||
});
|
||||
|
||||
// Обновляем состояние аутентификации
|
||||
auth.isAuthenticated = true;
|
||||
auth.user = { email: email.value };
|
||||
auth.authType = 'email';
|
||||
|
||||
// Сбрасываем флаг показа сообщения с опциями авторизации
|
||||
hasShownAuthMessage.value = false;
|
||||
} catch (error) {
|
||||
console.error('Error verifying email code:', error);
|
||||
|
||||
messages.value.push({
|
||||
sender: 'ai',
|
||||
text: 'Произошла ошибка при проверке кода. Пожалуйста, попробуйте позже.',
|
||||
timestamp: new Date(),
|
||||
});
|
||||
if (response.success) {
|
||||
showEmailVerification.value = true;
|
||||
// Временно для тестирования
|
||||
if (response.verificationCode) {
|
||||
emailErrorMessage.value = `Код для тестирования: ${response.verificationCode}`;
|
||||
}
|
||||
} else {
|
||||
emailErrorMessage.value = response.error || 'Ошибка запроса кода подтверждения';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error connecting with email:', error);
|
||||
|
||||
messages.value.push({
|
||||
sender: 'ai',
|
||||
text: 'Извините, произошла ошибка при подключении Email. Пожалуйста, попробуйте позже.',
|
||||
timestamp: new Date(),
|
||||
});
|
||||
console.error('Error requesting email verification:', error);
|
||||
emailErrorMessage.value = 'Ошибка запроса кода подтверждения';
|
||||
}
|
||||
}
|
||||
|
||||
// Подтверждение кода подтверждения по email
|
||||
async function verifyEmailCode() {
|
||||
emailErrorMessage.value = '';
|
||||
|
||||
try {
|
||||
const response = await auth.verifyEmail(emailVerificationCode.value);
|
||||
|
||||
if (response.success) {
|
||||
// Успешная верификация
|
||||
showEmailVerification.value = false;
|
||||
emailVerificationCode.value = '';
|
||||
|
||||
// Связываем гостевые сообщения с аутентифицированным пользователем
|
||||
try {
|
||||
await axios.post('/api/chat/link-guest-messages');
|
||||
console.log('Guest messages linked to authenticated user');
|
||||
} catch (linkError) {
|
||||
console.error('Error linking guest messages:', linkError);
|
||||
}
|
||||
|
||||
// Загружаем историю сообщений
|
||||
await loadChatHistory();
|
||||
} else {
|
||||
emailErrorMessage.value = response.error || 'Неверный код подтверждения';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error verifying email code:', error);
|
||||
emailErrorMessage.value = 'Ошибка верификации';
|
||||
}
|
||||
}
|
||||
|
||||
// Отмена верификации email
|
||||
function cancelEmailVerification() {
|
||||
showEmailVerification.value = false;
|
||||
emailVerificationCode.value = '';
|
||||
emailErrorMessage.value = '';
|
||||
}
|
||||
|
||||
// Добавьте эту функцию в <script setup>
|
||||
const formatAddress = (address) => {
|
||||
if (!address) return '';
|
||||
@@ -856,4 +853,14 @@ h1 {
|
||||
background-color: #4caf50;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
background-color: #999;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: #D32F2F;
|
||||
font-size: 0.9rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user