Описание изменений
This commit is contained in:
@@ -20,9 +20,16 @@ const adminRoutes = require('./routes/admin');
|
||||
|
||||
const app = express();
|
||||
|
||||
// Указываем хост явно
|
||||
app.set('host', '127.0.0.1');
|
||||
app.set('port', process.env.PORT || 8000);
|
||||
|
||||
// Настройка CORS
|
||||
app.use(cors({
|
||||
origin: 'http://localhost:5173',
|
||||
origin: [
|
||||
'http://localhost:5173',
|
||||
'http://127.0.0.1:5173' // Добавляем альтернативный origin
|
||||
],
|
||||
credentials: true,
|
||||
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
||||
allowedHeaders: ['Content-Type', 'Authorization', 'Cookie']
|
||||
|
||||
@@ -65,10 +65,11 @@ app.get('/api/health', (req, res) => {
|
||||
});
|
||||
|
||||
// Запуск сервера
|
||||
app.listen(PORT, async () => {
|
||||
const host = app.get('host');
|
||||
app.listen(PORT, host, async () => {
|
||||
try {
|
||||
await initServices();
|
||||
console.log('Server is running on port', PORT);
|
||||
console.log(`Server is running on http://${host}:${PORT}`);
|
||||
} catch (error) {
|
||||
console.error('Error starting server:', error);
|
||||
process.exit(1);
|
||||
|
||||
@@ -68,6 +68,7 @@ class TelegramBotService {
|
||||
} catch (error) {
|
||||
logger.error(`Error sending welcome message to ${chatId}:`, error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async handleVerificationCode(msg) {
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
"buffer": "^6.0.3",
|
||||
"connect-pg-simple": "^10.0.0",
|
||||
"ethers": "6.13.5",
|
||||
"pinia": "^2.0.33",
|
||||
"siwe": "^2.1.4",
|
||||
"sortablejs": "^1.15.6",
|
||||
"vue": "^3.2.47",
|
||||
|
||||
@@ -5,29 +5,60 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted } from 'vue';
|
||||
import { useAuthStore } from './stores/auth';
|
||||
import { onMounted, ref, provide, computed } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import axios from 'axios';
|
||||
|
||||
console.log('App.vue: Version with auth check loaded');
|
||||
|
||||
const authStore = useAuthStore();
|
||||
const router = useRouter();
|
||||
|
||||
async function checkAuth() {
|
||||
try {
|
||||
const response = await axios.get('/api/auth/check');
|
||||
// Создаем реактивное состояние с помощью ref
|
||||
const authState = ref({
|
||||
isAuthenticated: false,
|
||||
userRole: null,
|
||||
address: null
|
||||
});
|
||||
|
||||
if (response.data.authenticated) {
|
||||
authStore.setAuth(response.data);
|
||||
// Предоставляем состояние аутентификации всем компонентам
|
||||
const auth = {
|
||||
// Используем computed для реактивности
|
||||
isAuthenticated: computed(() => authState.value.isAuthenticated),
|
||||
userRole: computed(() => authState.value.userRole),
|
||||
address: computed(() => authState.value.address),
|
||||
async checkAuth() {
|
||||
try {
|
||||
const response = await axios.get('/api/auth/check');
|
||||
console.log('Auth check response:', response.data);
|
||||
authState.value = {
|
||||
isAuthenticated: response.data.authenticated,
|
||||
userRole: response.data.role,
|
||||
address: response.data.address
|
||||
};
|
||||
console.log('Auth state updated:', authState.value);
|
||||
} catch (error) {
|
||||
console.error('Auth check failed:', error);
|
||||
}
|
||||
},
|
||||
async disconnect() {
|
||||
try {
|
||||
await axios.post('/api/auth/logout');
|
||||
authState.value = {
|
||||
isAuthenticated: false,
|
||||
userRole: null,
|
||||
address: null
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Logout failed:', error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking auth:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(checkAuth);
|
||||
provide('auth', auth);
|
||||
|
||||
onMounted(async () => {
|
||||
await auth.checkAuth();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import axios from 'axios';
|
||||
import { useAuthStore } from '../stores/auth';
|
||||
|
||||
// Создаем экземпляр axios с базовым URL
|
||||
const api = axios.create({
|
||||
baseURL: '', // Убираем baseURL
|
||||
baseURL: import.meta.env.VITE_API_URL || '',
|
||||
withCredentials: true,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
@@ -15,10 +14,6 @@ api.interceptors.request.use(
|
||||
(config) => {
|
||||
config.withCredentials = true; // Важно для каждого запроса
|
||||
|
||||
const authStore = useAuthStore();
|
||||
if (authStore.isAuthenticated && authStore.address) {
|
||||
config.headers.Authorization = `Bearer ${authStore.address}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => Promise.reject(error)
|
||||
@@ -30,15 +25,11 @@ api.interceptors.response.use(
|
||||
console.log('Response from server:', response.data);
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
async (error) => {
|
||||
// Проверяем, что это действительно ошибка авторизации
|
||||
if (error.response?.status === 401 &&
|
||||
!error.config.url.includes('/auth/') &&
|
||||
!error.config.url.includes('/verify') &&
|
||||
!error.config.url.includes('/chat/history')) { // Не очищаем при ошибке загрузки истории
|
||||
console.log('Auth error, clearing state');
|
||||
const auth = useAuthStore();
|
||||
auth.disconnect();
|
||||
if (error.response?.status === 401) {
|
||||
// Перенаправляем на страницу логина
|
||||
window.location.href = '/login';
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div v-if="authStore.isAuthenticated">
|
||||
<div v-if="isAuthenticated">
|
||||
<div class="conversation-list">
|
||||
<div class="list-header">
|
||||
<h3>Диалоги</h3>
|
||||
@@ -40,20 +40,20 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, computed, defineEmits, watch } from 'vue';
|
||||
import { useAuthStore } from '../../stores/auth';
|
||||
import { ref, onMounted, computed, defineEmits, watch, inject } from 'vue';
|
||||
import axios from 'axios';
|
||||
|
||||
const emit = defineEmits(['select-conversation']);
|
||||
const authStore = useAuthStore();
|
||||
const auth = inject('auth');
|
||||
const isAuthenticated = computed(() => auth.isAuthenticated.value);
|
||||
|
||||
const conversations = ref([]);
|
||||
const loading = ref(true);
|
||||
const selectedConversationId = ref(null);
|
||||
|
||||
// Следим за изменением статуса аутентификации
|
||||
watch(() => authStore.isAuthenticated, (isAuthenticated) => {
|
||||
if (!isAuthenticated) {
|
||||
watch(() => isAuthenticated.value, (authenticated) => {
|
||||
if (!authenticated) {
|
||||
conversations.value = []; // Очищаем список бесед при отключении
|
||||
selectedConversationId.value = null;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div v-if="authStore.isAuthenticated">
|
||||
<div v-if="isAuthenticated">
|
||||
<div class="message-thread" ref="threadContainer">
|
||||
<div v-if="loading" class="loading">Загрузка сообщений...</div>
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, watch, nextTick, defineExpose } from 'vue';
|
||||
import axios from 'axios';
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
|
||||
const props = defineProps({
|
||||
conversationId: {
|
||||
@@ -40,7 +39,7 @@ const props = defineProps({
|
||||
const messages = ref([]);
|
||||
const loading = ref(true);
|
||||
const threadContainer = ref(null);
|
||||
const authStore = useAuthStore()
|
||||
const isAuthenticated = ref(false);
|
||||
|
||||
// Загрузка сообщений диалога
|
||||
const fetchMessages = async () => {
|
||||
@@ -112,8 +111,8 @@ watch(
|
||||
);
|
||||
|
||||
// Следим за изменением статуса аутентификации
|
||||
watch(() => authStore.isAuthenticated, (isAuthenticated) => {
|
||||
if (!isAuthenticated) {
|
||||
watch(() => isAuthenticated.value, (authenticated) => {
|
||||
if (!authenticated) {
|
||||
messages.value = []; // Очищаем сообщения при отключении
|
||||
}
|
||||
});
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import axios from 'axios';
|
||||
|
||||
const props = defineProps({
|
||||
onEmailAuth: {
|
||||
@@ -49,6 +50,7 @@ const code = ref('');
|
||||
const error = ref('');
|
||||
const isLoading = ref(false);
|
||||
const showVerification = ref(false);
|
||||
const isConnecting = ref(false);
|
||||
|
||||
const isValidEmail = computed(() => {
|
||||
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value);
|
||||
|
||||
@@ -18,6 +18,7 @@ import axios from 'axios';
|
||||
const loading = ref(false);
|
||||
const error = ref('');
|
||||
const success = ref('');
|
||||
const isConnecting = ref(false);
|
||||
|
||||
async function connectTelegram() {
|
||||
try {
|
||||
|
||||
@@ -5,14 +5,17 @@
|
||||
:disabled="isLoading"
|
||||
class="wallet-btn"
|
||||
>
|
||||
{{ isAuthenticated ? 'Подключено' : 'Подключить кошелек' }}
|
||||
{{ isConnected ? 'Подключено' : 'Подключить кошелек' }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { ref, inject, computed } from 'vue';
|
||||
import { connectWithWallet } from '../../services/wallet';
|
||||
import { ethers } from 'ethers';
|
||||
import { SiweMessage } from 'siwe';
|
||||
import axios from 'axios';
|
||||
|
||||
// Определяем props
|
||||
const props = defineProps({
|
||||
@@ -24,6 +27,12 @@ const props = defineProps({
|
||||
|
||||
// Определяем состояние
|
||||
const isLoading = ref(false);
|
||||
const auth = inject('auth');
|
||||
const isConnecting = ref(false);
|
||||
const address = ref('');
|
||||
|
||||
// Вычисляемое свойство для статуса подключения
|
||||
const isConnected = computed(() => auth.isAuthenticated.value);
|
||||
|
||||
const emit = defineEmits(['connect']);
|
||||
|
||||
@@ -33,22 +42,10 @@ const connectWallet = async () => {
|
||||
|
||||
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 });
|
||||
const result = await connectWithWallet();
|
||||
await auth.checkAuth();
|
||||
console.log('Wallet connected, auth state:', auth.isAuthenticated.value);
|
||||
emit('connect', result);
|
||||
} catch (error) {
|
||||
console.error('Error connecting wallet:', error);
|
||||
} finally {
|
||||
|
||||
@@ -2,20 +2,17 @@ import { Buffer } from 'buffer';
|
||||
globalThis.Buffer = Buffer;
|
||||
|
||||
import { createApp } from 'vue';
|
||||
import { createPinia } from 'pinia';
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
import axios from 'axios';
|
||||
|
||||
// Настройка axios
|
||||
axios.defaults.baseURL = ''; // Пустой baseURL, так как мы используем прокси
|
||||
axios.defaults.withCredentials = true; // Важно для работы с сессиями
|
||||
axios.defaults.baseURL = import.meta.env.VITE_API_URL || '';
|
||||
axios.defaults.withCredentials = true;
|
||||
|
||||
// Создаем и монтируем приложение Vue
|
||||
const app = createApp(App);
|
||||
const pinia = createPinia();
|
||||
|
||||
app.use(pinia);
|
||||
app.use(router);
|
||||
|
||||
// Не используем заглушки, так как сервер работает
|
||||
@@ -29,7 +26,7 @@ app.use(router);
|
||||
// }
|
||||
|
||||
console.log('API URL:', import.meta.env.VITE_API_URL);
|
||||
console.log('main.js: Starting application with router and Pinia');
|
||||
console.log('main.js: Starting application with router');
|
||||
|
||||
app.mount('#app');
|
||||
console.log('main.js: Application with router and Pinia mounted');
|
||||
console.log('main.js: Application with router mounted');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
import HomeView from '../views/HomeView.vue';
|
||||
import { useAuthStore } from '../stores/auth';
|
||||
import axios from 'axios';
|
||||
|
||||
console.log('router/index.js: Script loaded');
|
||||
|
||||
@@ -21,8 +21,6 @@ console.log('router/index.js: Router created');
|
||||
|
||||
// Защита маршрутов
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
const authStore = useAuthStore();
|
||||
|
||||
// Если пытаемся перейти на несуществующий маршрут, перенаправляем на главную
|
||||
if (!to.matched.length) {
|
||||
return next({ name: 'home' });
|
||||
@@ -30,17 +28,19 @@ router.beforeEach(async (to, from, next) => {
|
||||
|
||||
// Проверяем аутентификацию, если маршрут требует авторизации
|
||||
if (to.matched.some(record => record.meta.requiresAuth)) {
|
||||
if (!authStore.isAuthenticated) {
|
||||
return next({ name: 'home' });
|
||||
}
|
||||
|
||||
// Проверяем права администратора
|
||||
if (to.matched.some(record => record.meta.requiresAdmin) && !authStore.isAdmin) {
|
||||
return next({ name: 'home' });
|
||||
try {
|
||||
const response = await axios.get('/api/auth/check');
|
||||
if (response.data.authenticated) {
|
||||
next();
|
||||
} else {
|
||||
next('/login');
|
||||
}
|
||||
} catch (error) {
|
||||
next('/login');
|
||||
}
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { ethers } from 'ethers';
|
||||
import api from '../api/axios';
|
||||
import { useAuthStore } from '../stores/auth';
|
||||
import axios from 'axios';
|
||||
|
||||
export async function connectWithWallet() {
|
||||
try {
|
||||
@@ -24,7 +23,7 @@ export async function connectWithWallet() {
|
||||
console.log('Normalized address:', address);
|
||||
|
||||
console.log('Requesting nonce...');
|
||||
const { data: { nonce } } = await api.get('/api/auth/nonce', {
|
||||
const { data: { nonce } } = await axios.get('/api/auth/nonce', {
|
||||
params: { address }
|
||||
});
|
||||
console.log('Got nonce:', nonce);
|
||||
@@ -58,19 +57,17 @@ export async function connectWithWallet() {
|
||||
console.log('Got signature:', signature);
|
||||
|
||||
console.log('Sending verification request...');
|
||||
const response = await api.post('/api/auth/verify', {
|
||||
const response = await axios.post('/api/auth/verify', {
|
||||
address,
|
||||
signature,
|
||||
message
|
||||
});
|
||||
console.log('Verification response:', response.data);
|
||||
|
||||
const authStore = useAuthStore();
|
||||
if (response.data.authenticated) {
|
||||
authStore.setAuth(response.data);
|
||||
}
|
||||
const provider = new ethers.BrowserProvider(window.ethereum);
|
||||
const signer = await provider.getSigner();
|
||||
|
||||
return response.data;
|
||||
return { address, signer };
|
||||
} catch (error) {
|
||||
// Форматируем ошибку для пользователя
|
||||
const message = error.message || 'Ошибка подключения кошелька';
|
||||
|
||||
@@ -1,515 +0,0 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import axios from '../api/axios';
|
||||
|
||||
export const useAuthStore = defineStore('auth', {
|
||||
state: () => ({
|
||||
user: null,
|
||||
isAuthenticated: false,
|
||||
isAdmin: false,
|
||||
authType: null,
|
||||
identities: {},
|
||||
loading: false,
|
||||
error: null,
|
||||
messages: [],
|
||||
address: null,
|
||||
wallet: null,
|
||||
telegramId: null,
|
||||
email: null,
|
||||
userId: null
|
||||
}),
|
||||
|
||||
actions: {
|
||||
async connectWallet(address, signature, message) {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await axios.post('/api/chat/verify', {
|
||||
address,
|
||||
signature,
|
||||
message
|
||||
}, {
|
||||
withCredentials: true
|
||||
});
|
||||
|
||||
this.user = {
|
||||
id: response.data.userId,
|
||||
address: address
|
||||
};
|
||||
this.isAuthenticated = response.data.authenticated;
|
||||
this.isAdmin = response.data.isAdmin;
|
||||
this.authType = 'wallet';
|
||||
|
||||
// Сохраняем адрес кошелька в локальном хранилище
|
||||
console.log('Saving wallet address to localStorage:', address);
|
||||
localStorage.setItem('walletAddress', address);
|
||||
|
||||
// Связываем гостевые сообщения с аутентифицированным пользователем
|
||||
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);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
authenticated: response.data.authenticated,
|
||||
address: address,
|
||||
isAdmin: response.data.isAdmin,
|
||||
authType: response.data.authType
|
||||
};
|
||||
} catch (error) {
|
||||
this.error = error.response?.data?.error || 'Ошибка подключения кошелька';
|
||||
return { success: false, error: this.error };
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async connectTelegram(telegramData) {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await axios.post('/api/auth/telegram', telegramData);
|
||||
|
||||
this.user = {
|
||||
id: response.data.userId,
|
||||
telegramId: telegramData.telegramId
|
||||
};
|
||||
this.isAuthenticated = response.data.authenticated;
|
||||
this.isAdmin = response.data.isAdmin;
|
||||
this.authType = response.data.authType;
|
||||
this.identities = response.data.identities || {};
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
this.error = error.response?.data?.error || 'Ошибка подключения Telegram';
|
||||
return false;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async connectEmail(email, verificationCode) {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await axios.post('/api/auth/email', {
|
||||
email, verificationCode
|
||||
});
|
||||
|
||||
this.user = {
|
||||
id: response.data.userId,
|
||||
email
|
||||
};
|
||||
this.isAuthenticated = response.data.authenticated;
|
||||
this.isAdmin = response.data.isAdmin;
|
||||
this.authType = response.data.authType;
|
||||
this.identities = response.data.identities || {};
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
this.error = error.response?.data?.error || 'Ошибка подключения Email';
|
||||
return false;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async linkIdentity(identityType, identityValue) {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await axios.post('/api/auth/link-identity', {
|
||||
identityType, identityValue
|
||||
});
|
||||
|
||||
this.identities = response.data.identities;
|
||||
this.isAdmin = response.data.isAdmin;
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
this.error = error.response?.data?.error || 'Ошибка связывания аккаунта';
|
||||
return false;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async logout() {
|
||||
try {
|
||||
await axios.post('/api/auth/logout');
|
||||
this.user = null;
|
||||
this.isAuthenticated = false;
|
||||
this.isAdmin = false;
|
||||
this.authType = null;
|
||||
this.identities = {};
|
||||
this.messages = [];
|
||||
this.address = null;
|
||||
|
||||
// Удаляем адрес из localStorage
|
||||
localStorage.removeItem('walletAddress');
|
||||
} catch (error) {
|
||||
console.error('Ошибка при выходе:', error);
|
||||
}
|
||||
},
|
||||
|
||||
async checkAuth() {
|
||||
try {
|
||||
console.log('Checking auth state...');
|
||||
const response = await axios.get('/api/auth/check');
|
||||
console.log('Auth check response:', response.data);
|
||||
|
||||
if (response.data.authenticated) {
|
||||
this.isAuthenticated = true;
|
||||
this.user = {
|
||||
id: response.data.userId,
|
||||
address: response.data.address
|
||||
};
|
||||
this.address = response.data.address;
|
||||
this.isAdmin = response.data.isAdmin;
|
||||
this.authType = response.data.authType;
|
||||
|
||||
return {
|
||||
authenticated: true,
|
||||
user: this.user,
|
||||
address: response.data.address,
|
||||
isAdmin: response.data.isAdmin,
|
||||
authType: response.data.authType
|
||||
};
|
||||
} else {
|
||||
this.isAuthenticated = false;
|
||||
this.user = null;
|
||||
this.address = null;
|
||||
this.isAdmin = false;
|
||||
this.authType = null;
|
||||
|
||||
return { authenticated: false };
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking auth:', error);
|
||||
this.isAuthenticated = false;
|
||||
this.user = null;
|
||||
this.address = null;
|
||||
this.isAdmin = false;
|
||||
this.authType = null;
|
||||
|
||||
return { authenticated: false };
|
||||
}
|
||||
},
|
||||
|
||||
async refreshSession() {
|
||||
try {
|
||||
// Если есть адрес в localStorage, используем его
|
||||
const storedAddress = localStorage.getItem('walletAddress');
|
||||
|
||||
const response = await axios.post('/api/auth/refresh-session', {
|
||||
address: storedAddress || this.address
|
||||
}, {
|
||||
withCredentials: true
|
||||
});
|
||||
|
||||
return response.data.success;
|
||||
} catch (error) {
|
||||
console.error('Error refreshing session:', error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
async checkWalletConnection() {
|
||||
// Проверяем, есть ли сохраненный адрес кошелька
|
||||
const savedAddress = localStorage.getItem('walletAddress');
|
||||
console.log('Checking for saved wallet address:', savedAddress);
|
||||
|
||||
if (savedAddress) {
|
||||
try {
|
||||
// Проверяем, доступен ли провайдер Ethereum (MetaMask)
|
||||
if (window.ethereum) {
|
||||
// Запрашиваем доступ к аккаунтам
|
||||
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
|
||||
const currentAddress = accounts[0].toLowerCase();
|
||||
|
||||
console.log('Current connected address:', currentAddress);
|
||||
console.log('Saved address:', savedAddress.toLowerCase());
|
||||
|
||||
// Проверяем, совпадает ли текущий адрес с сохраненным
|
||||
if (currentAddress === savedAddress.toLowerCase()) {
|
||||
console.log('Wallet address matches, restoring session');
|
||||
|
||||
// Восстанавливаем состояние аутентификации
|
||||
this.user = {
|
||||
id: null, // ID будет получен при проверке аутентификации
|
||||
address: savedAddress
|
||||
};
|
||||
|
||||
// Проверяем аутентификацию на сервере
|
||||
const authResult = await this.checkAuth();
|
||||
|
||||
if (authResult.authenticated) {
|
||||
console.log('Session restored successfully');
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
console.log('Connected wallet address does not match saved address');
|
||||
localStorage.removeItem('walletAddress');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error restoring wallet connection:', error);
|
||||
}
|
||||
}
|
||||
|
||||
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: 'Ошибка проверки статуса' };
|
||||
}
|
||||
},
|
||||
|
||||
async disconnect() {
|
||||
try {
|
||||
// Очищаем сессию на сервере
|
||||
await axios.post('/api/auth/clear-session');
|
||||
|
||||
// Очищаем состояние
|
||||
this.isAuthenticated = false;
|
||||
this.userId = null;
|
||||
this.address = null;
|
||||
this.isAdmin = false;
|
||||
this.authType = null;
|
||||
|
||||
// Очищаем локальное хранилище
|
||||
localStorage.removeItem('auth');
|
||||
} catch (error) {
|
||||
console.error('Error during disconnect:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// Выносим очистку состояния в отдельный метод
|
||||
clearState() {
|
||||
this.isAuthenticated = false;
|
||||
this.wallet = null;
|
||||
this.messages = [];
|
||||
this.user = null;
|
||||
this.address = null;
|
||||
this.isAdmin = false;
|
||||
this.authType = null;
|
||||
this.identities = {};
|
||||
this.telegramId = null;
|
||||
this.userId = null;
|
||||
|
||||
// Очищаем локальное хранилище
|
||||
localStorage.removeItem('wallet');
|
||||
localStorage.removeItem('isAuthenticated');
|
||||
localStorage.removeItem('walletAddress');
|
||||
localStorage.removeItem('auth');
|
||||
},
|
||||
|
||||
async setWalletAuth(authData) {
|
||||
this.isAuthenticated = authData.authenticated;
|
||||
this.address = authData.address;
|
||||
this.isAdmin = authData.isAdmin;
|
||||
},
|
||||
|
||||
async setTelegramAuth(authData) {
|
||||
this.telegramId = authData.telegramId;
|
||||
// Проверяем баланс токенов если есть подключенный кошелек
|
||||
if (this.address) {
|
||||
await this.checkTokenBalance();
|
||||
}
|
||||
},
|
||||
|
||||
async checkTokenBalance() {
|
||||
try {
|
||||
const response = await axios.get(`/api/auth/check-tokens?address=${this.address}`);
|
||||
this.isAdmin = response.data.isAdmin;
|
||||
} catch (error) {
|
||||
console.error('Error checking token balance:', error);
|
||||
}
|
||||
},
|
||||
|
||||
setAuth(authData) {
|
||||
console.log('Setting auth state:', authData);
|
||||
|
||||
// Обновляем только состояние в памяти
|
||||
this.isAuthenticated = authData.authenticated || authData.isAuthenticated;
|
||||
this.user = {
|
||||
id: authData.userId,
|
||||
address: authData.address
|
||||
};
|
||||
this.userId = authData.userId;
|
||||
this.isAdmin = authData.isAdmin;
|
||||
this.authType = authData.authType;
|
||||
this.address = authData.address;
|
||||
|
||||
console.log('Auth state updated:', {
|
||||
isAuthenticated: this.isAuthenticated,
|
||||
userId: this.userId,
|
||||
authType: this.authType,
|
||||
address: this.address,
|
||||
isAdmin: this.isAdmin
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="home">
|
||||
<h1>✌️ HB3 - Accelerator DLE (Digital Legal Entity - DAO Fork)</h1>
|
||||
|
||||
<div class="auth-section" v-if="!auth.isAuthenticated">
|
||||
<div class="auth-section" v-if="!isAuthenticated">
|
||||
<h3>Венчурный фонд и поставщик программного обеспечения</h3>
|
||||
</div>
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
<div class="chat-container">
|
||||
<div class="chat-header">
|
||||
<!-- Используем тот же компонент, что и в сообщениях -->
|
||||
<div v-if="!auth.isAuthenticated" class="auth-buttons">
|
||||
<div v-if="!isAuthenticated" class="auth-buttons">
|
||||
<button class="auth-btn wallet-btn" @click="handleWalletAuth">
|
||||
<span class="auth-icon">👛</span> Подключить кошелек
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="wallet-info">
|
||||
<span>{{ truncateAddress(auth.address) }}</span>
|
||||
<span>{{ truncateAddress(auth.address.value) }}</span>
|
||||
<button class="disconnect-btn" @click="disconnectWallet">
|
||||
Отключить кошелек
|
||||
</button>
|
||||
@@ -24,7 +24,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Кнопка загрузки предыдущих сообщений -->
|
||||
<div v-if="auth.isAuthenticated && hasMoreMessages" class="load-more">
|
||||
<div v-if="isAuthenticated && hasMoreMessages" class="load-more">
|
||||
<button @click="loadMoreMessages" :disabled="isLoadingMore">
|
||||
{{ isLoadingMore ? 'Загрузка...' : 'Показать предыдущие сообщения' }}
|
||||
</button>
|
||||
@@ -40,7 +40,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Кнопки аутентификации -->
|
||||
<div v-if="message.showAuthButtons && !auth.isAuthenticated" class="auth-buttons">
|
||||
<div v-if="message.showAuthButtons && !isAuthenticated" class="auth-buttons">
|
||||
<button class="auth-btn wallet-btn" @click="handleWalletAuth">
|
||||
<span class="auth-icon">👛</span> Подключить кошелек
|
||||
</button>
|
||||
@@ -117,16 +117,18 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, watch, nextTick, onBeforeUnmount } from 'vue';
|
||||
import { useAuthStore } from '../stores/auth';
|
||||
import { ref, computed, onMounted, watch, nextTick, onBeforeUnmount, inject } from 'vue';
|
||||
import WalletConnection from '../components/identity/WalletConnection.vue';
|
||||
import TelegramConnect from '../components/identity/TelegramConnect.vue';
|
||||
import EmailConnect from '../components/identity/EmailConnect.vue';
|
||||
import api from '../api/axios';
|
||||
import { connectWithWallet } from '../services/wallet';
|
||||
|
||||
console.log('HomeView.vue: Version with chat loaded');
|
||||
|
||||
const auth = useAuthStore();
|
||||
const auth = inject('auth');
|
||||
const isAuthenticated = computed(() => auth.isAuthenticated.value);
|
||||
const authType = ref(null);
|
||||
const messages = ref([]);
|
||||
const guestMessages = ref([]);
|
||||
const newMessage = ref('');
|
||||
@@ -181,7 +183,7 @@ const scrollToBottom = () => {
|
||||
|
||||
// Загрузка сообщений
|
||||
const loadMoreMessages = async () => {
|
||||
if (!auth.isAuthenticated) return;
|
||||
if (!isAuthenticated.value) return;
|
||||
|
||||
try {
|
||||
isLoadingMore.value = true;
|
||||
@@ -213,7 +215,7 @@ const loadMoreMessages = async () => {
|
||||
};
|
||||
|
||||
// Загружаем сообщения при изменении аутентификации
|
||||
watch(() => auth.isAuthenticated, async (newValue) => {
|
||||
watch(() => isAuthenticated.value, async (newValue) => {
|
||||
if (newValue) {
|
||||
messages.value = [];
|
||||
offset.value = 0;
|
||||
@@ -244,12 +246,13 @@ watch(() => auth.isAuthenticated, async (newValue) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Функция для подключения кошелька
|
||||
// Находим существующую функцию handleWalletAuth и обновляем её
|
||||
const handleWalletAuth = async () => {
|
||||
try {
|
||||
const result = await connectWithWallet();
|
||||
await auth.checkAuth();
|
||||
|
||||
if (result.success) {
|
||||
if (result.authenticated) {
|
||||
// Сохраняем гостевые сообщения перед очисткой
|
||||
const guestMessages = [...messages.value];
|
||||
messages.value = [];
|
||||
@@ -432,7 +435,7 @@ const handleMessage = async (text) => {
|
||||
newMessage.value = '';
|
||||
isLoading.value = true;
|
||||
|
||||
if (!auth.isAuthenticated) {
|
||||
if (!isAuthenticated.value) {
|
||||
// Сохраняем в таблицу guest_messages
|
||||
const response = await api.post('/api/chat/guest-message', {
|
||||
message: messageContent,
|
||||
|
||||
@@ -38,10 +38,10 @@ export default defineConfig({
|
||||
},
|
||||
server: {
|
||||
port: 5173,
|
||||
host: 'localhost',
|
||||
host: '127.0.0.1',
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8000',
|
||||
target: 'http://127.0.0.1:8000',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
credentials: true,
|
||||
|
||||
Reference in New Issue
Block a user