diff --git a/backend/migrations/cleanup.sql b/backend/migrations/cleanup.sql new file mode 100644 index 0000000..a3c99ca --- /dev/null +++ b/backend/migrations/cleanup.sql @@ -0,0 +1,21 @@ +-- Создаем временную таблицу для уникальных адресов +CREATE TEMP TABLE unique_users AS +SELECT DISTINCT ON (LOWER(address)) + id, + LOWER(address) as address, + created_at +FROM users +ORDER BY LOWER(address), created_at ASC; + +-- Удаляем все записи из users +TRUNCATE users CASCADE; + +-- Восстанавливаем уникальные записи +INSERT INTO users (id, address, created_at) +SELECT id, address, created_at FROM unique_users; + +-- Обновляем последовательность id +SELECT setval('users_id_seq', (SELECT MAX(id) FROM users)); + +-- Удаляем временную таблицу +DROP TABLE unique_users; \ No newline at end of file diff --git a/backend/migrations/init.sql b/backend/migrations/init.sql index 074876a..9341fb4 100644 --- a/backend/migrations/init.sql +++ b/backend/migrations/init.sql @@ -29,4 +29,12 @@ CREATE TABLE IF NOT EXISTS chat_history ( -- Даем права пользователю GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO postgres; -GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO postgres; \ No newline at end of file +GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO postgres; + +-- Обновляем существующие адреса +UPDATE users SET address = LOWER(address); + +-- Удаляем дубликаты +DELETE FROM users a USING users b +WHERE a.id > b.id +AND LOWER(a.address) = LOWER(b.address); \ No newline at end of file diff --git a/backend/routes/api.js b/backend/routes/api.js index 28fbf72..4a25f35 100644 --- a/backend/routes/api.js +++ b/backend/routes/api.js @@ -130,7 +130,7 @@ router.post('/chat', requireAuth, async (req, res) => { // Получаем или создаем пользователя let user = await pool.query( - 'INSERT INTO users (address) VALUES ($1) ON CONFLICT (address) DO UPDATE SET address = EXCLUDED.address RETURNING id', + 'INSERT INTO users (address) VALUES (LOWER($1)) ON CONFLICT (address) DO UPDATE SET address = LOWER($1) RETURNING id', [userAddress] ); const userId = user.rows[0].id; @@ -166,34 +166,36 @@ router.post('/chat', requireAuth, async (req, res) => { router.get('/chat/history', requireAuth, async (req, res) => { try { const userAddress = req.session.siwe.address; + console.log('Запрос истории чата для:', userAddress); // Получаем ID пользователя const userResult = await pool.query( - 'SELECT id FROM users WHERE address = $1', + 'SELECT id FROM users WHERE LOWER(address) = LOWER($1) ORDER BY created_at ASC LIMIT 1', [userAddress] ); + console.log('Найден пользователь:', userResult.rows); + + if (userResult.rows.length === 0) { + return res.status(404).json({ error: 'User not found' }); + } + const userId = userResult.rows[0].id; // Получаем историю чата const history = await pool.query( - `SELECT * FROM ( - SELECT - message as content, - created_at, - 'user' as role - FROM chat_history - WHERE user_id = $1 - UNION ALL - SELECT - response as content, - created_at, - 'assistant' as role - FROM chat_history - WHERE user_id = $1 - ) messages - ORDER BY created_at ASC`, + `SELECT + ch.id, + LOWER(u.address) as address, + ch.message, + ch.response, + ch.created_at + FROM chat_history ch + JOIN users u ON ch.user_id = u.id + WHERE ch.user_id = $1 + ORDER BY created_at DESC`, [userId] ); + console.log('История чата:', history.rows); res.json({ history: history.rows @@ -204,4 +206,19 @@ router.get('/chat/history', requireAuth, async (req, res) => { } }); +// Получение списка пользователей +router.get('/users', requireAuth, async (req, res) => { + try { + console.log('Запрос списка пользователей'); + const users = await pool.query( + 'SELECT id, LOWER(address) as address, created_at FROM users ORDER BY created_at DESC' + ); + console.log('Найдено пользователей:', users.rows); + res.json({ users: users.rows }); + } catch (error) { + console.error('Ошибка получения пользователей:', error); + res.status(500).json({ error: 'Ошибка сервера' }); + } +}); + module.exports = router; \ No newline at end of file diff --git a/backend/sessions/MQ7gY14YfnCv8X0bQKUNjZjITzdTWz58.json b/backend/sessions/MQ7gY14YfnCv8X0bQKUNjZjITzdTWz58.json index e01e81f..2777518 100644 --- a/backend/sessions/MQ7gY14YfnCv8X0bQKUNjZjITzdTWz58.json +++ b/backend/sessions/MQ7gY14YfnCv8X0bQKUNjZjITzdTWz58.json @@ -1 +1 @@ -{"cookie":{"originalMaxAge":2591999999,"expires":"2025-03-23T14:05:28.613Z","secure":false,"httpOnly":true,"domain":"127.0.0.1","path":"/","sameSite":"lax"},"nonce":null,"__lastAccess":1740146728614,"siwe":{"domain":"127.0.0.1:5173","address":"0xF45aa4917b3775bA37f48Aeb3dc1a943561e9e0B","statement":"Sign in with Ethereum to access DApp features and AI Assistant","uri":"http://127.0.0.1:5173","version":"1","nonce":"KF6YuwJBLPv8vFKCK","issuedAt":"2025-02-21T14:05:01.684Z","chainId":11155111,"resources":["http://127.0.0.1:5173/api/chat","http://127.0.0.1:5173/api/contract"]},"authenticated":true} \ No newline at end of file +{"cookie":{"originalMaxAge":2591999999,"expires":"2025-03-23T16:06:19.831Z","secure":false,"httpOnly":true,"domain":"127.0.0.1","path":"/","sameSite":"lax"},"nonce":null,"__lastAccess":1740153979832,"siwe":{"domain":"127.0.0.1:5173","address":"0xF45aa4917b3775bA37f48Aeb3dc1a943561e9e0B","statement":"Sign in with Ethereum to access DApp features and AI Assistant","uri":"http://127.0.0.1:5173","version":"1","nonce":"u7P3wT2kyPmGb4Z0I","issuedAt":"2025-02-21T16:05:49.561Z","chainId":11155111,"resources":["http://127.0.0.1:5173/api/chat","http://127.0.0.1:5173/api/contract"]},"authenticated":true} \ No newline at end of file diff --git a/backend/sessions/OgJhv73wKRJ4xuAAUBa9k5CtZ9E4dcFJ.json b/backend/sessions/OgJhv73wKRJ4xuAAUBa9k5CtZ9E4dcFJ.json new file mode 100644 index 0000000..b710c12 --- /dev/null +++ b/backend/sessions/OgJhv73wKRJ4xuAAUBa9k5CtZ9E4dcFJ.json @@ -0,0 +1 @@ +{"cookie":{"originalMaxAge":2592000000,"expires":"2025-03-23T14:54:20.531Z","secure":false,"httpOnly":true,"domain":"127.0.0.1","path":"/","sameSite":"lax"},"__lastAccess":1740149660531} \ No newline at end of file diff --git a/backend/sessions/P6j78qGOxf6uv3j-AGfgu0la3luxKbaV.json b/backend/sessions/P6j78qGOxf6uv3j-AGfgu0la3luxKbaV.json new file mode 100644 index 0000000..519ec36 --- /dev/null +++ b/backend/sessions/P6j78qGOxf6uv3j-AGfgu0la3luxKbaV.json @@ -0,0 +1 @@ +{"cookie":{"originalMaxAge":2592000000,"expires":"2025-03-23T14:53:08.151Z","secure":false,"httpOnly":true,"domain":"127.0.0.1","path":"/","sameSite":"lax"},"__lastAccess":1740149588151} \ No newline at end of file diff --git a/backend/sessions/VO2T1gK1S6E0sq7-yYtjHx6p3nsixeGv.json b/backend/sessions/VO2T1gK1S6E0sq7-yYtjHx6p3nsixeGv.json new file mode 100644 index 0000000..ba2a55a --- /dev/null +++ b/backend/sessions/VO2T1gK1S6E0sq7-yYtjHx6p3nsixeGv.json @@ -0,0 +1 @@ +{"cookie":{"originalMaxAge":2592000000,"expires":"2025-03-23T14:36:28.622Z","secure":false,"httpOnly":true,"domain":"127.0.0.1","path":"/","sameSite":"lax"},"__lastAccess":1740148588623} \ No newline at end of file diff --git a/backend/sessions/jK4QzdazTk9W_ubXZTLnIEwgGD2xcwmE.json b/backend/sessions/jK4QzdazTk9W_ubXZTLnIEwgGD2xcwmE.json new file mode 100644 index 0000000..6c28ad7 --- /dev/null +++ b/backend/sessions/jK4QzdazTk9W_ubXZTLnIEwgGD2xcwmE.json @@ -0,0 +1 @@ +{"cookie":{"originalMaxAge":2592000000,"expires":"2025-03-23T14:49:54.299Z","secure":false,"httpOnly":true,"domain":"127.0.0.1","path":"/","sameSite":"lax"},"__lastAccess":1740149394299} \ No newline at end of file diff --git a/frontend/src/App.vue b/frontend/src/App.vue index e91305b..7123ab3 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -5,8 +5,14 @@ + @@ -15,10 +21,12 @@ import { ref, watch } from 'vue' import ContractInteraction from './components/ContractInteraction.vue' import AIAssistant from './components/AIAssistant.vue' import ServerControl from './components/ServerControl.vue' +import DataTables from './components/DataTables.vue' const contractInteraction = ref(null) const isConnected = ref(false) const userAddress = ref(null) +const dataTables = ref(null) watch(() => contractInteraction.value?.isConnected, (newValue) => { isConnected.value = newValue @@ -27,6 +35,10 @@ watch(() => contractInteraction.value?.isConnected, (newValue) => { watch(() => contractInteraction.value?.address, (newValue) => { userAddress.value = newValue }) + +function handleChatUpdate() { + dataTables.value?.fetchData() +} \ No newline at end of file