Описание изменений

This commit is contained in:
2025-03-19 17:18:03 +03:00
parent 04d027054d
commit 55e4d81c95
75 changed files with 2103 additions and 4861 deletions

View File

@@ -0,0 +1,8 @@
CREATE TABLE IF NOT EXISTS "session" (
"sid" varchar NOT NULL COLLATE "default",
"sess" json NOT NULL,
"expire" timestamp(6) NOT NULL,
CONSTRAINT "session_pkey" PRIMARY KEY ("sid")
);
CREATE INDEX IF NOT EXISTS "IDX_session_expire" ON "session" ("expire");

View File

@@ -0,0 +1,9 @@
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username VARCHAR(255),
email VARCHAR(255) UNIQUE,
address VARCHAR(255) UNIQUE,
status VARCHAR(50) DEFAULT 'active',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

View File

@@ -0,0 +1,24 @@
CREATE TABLE IF NOT EXISTS roles (
id SERIAL PRIMARY KEY,
name VARCHAR(50) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Добавляем базовые роли
INSERT INTO roles (name) VALUES ('admin'), ('user')
ON CONFLICT (name) DO NOTHING;
-- Добавляем связь пользователей с ролями
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'users' AND column_name = 'role_id'
) THEN
ALTER TABLE users
ADD COLUMN role_id INTEGER REFERENCES roles(id);
END IF;
END $$;
-- Создаем индекс для role_id после добавления колонки
CREATE INDEX IF NOT EXISTS idx_users_role_id ON users(role_id);

View File

@@ -0,0 +1,28 @@
CREATE TABLE IF NOT EXISTS user_identities (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
provider VARCHAR(50) NOT NULL,
provider_id VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(provider, provider_id)
);
-- Создаем индексы после создания таблицы
DO $$
BEGIN
-- Индекс для user_id
IF NOT EXISTS (
SELECT 1 FROM pg_indexes
WHERE tablename = 'user_identities' AND indexname = 'idx_user_identities_user_id'
) THEN
CREATE INDEX idx_user_identities_user_id ON user_identities(user_id);
END IF;
-- Индекс для provider и provider_id
IF NOT EXISTS (
SELECT 1 FROM pg_indexes
WHERE tablename = 'user_identities' AND indexname = 'idx_user_identities_type_value'
) THEN
CREATE INDEX idx_user_identities_type_value ON user_identities(provider, provider_id);
END IF;
END $$;

View File

@@ -0,0 +1,10 @@
CREATE TABLE IF NOT EXISTS conversations (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
title VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_conversations_user_id ON conversations(user_id);
CREATE INDEX IF NOT EXISTS idx_conversations_created_at ON conversations(created_at);

View File

@@ -0,0 +1,20 @@
CREATE TABLE IF NOT EXISTS messages (
id SERIAL PRIMARY KEY,
conversation_id INTEGER REFERENCES conversations(id) ON DELETE CASCADE,
sender_type VARCHAR(20) NOT NULL,
sender_id INTEGER,
content TEXT,
channel VARCHAR(20) NOT NULL,
metadata JSONB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
role VARCHAR(20) NOT NULL DEFAULT 'user',
guest_message_id INTEGER,
tokens_used INTEGER DEFAULT 0,
is_processed BOOLEAN DEFAULT FALSE
);
CREATE INDEX IF NOT EXISTS idx_messages_conversation_id ON messages(conversation_id);
CREATE INDEX IF NOT EXISTS idx_messages_sender_type ON messages(sender_type);
CREATE INDEX IF NOT EXISTS idx_messages_created_at ON messages(created_at);
CREATE INDEX IF NOT EXISTS idx_messages_channel ON messages(channel);
CREATE INDEX IF NOT EXISTS idx_messages_metadata ON messages USING gin(metadata);

View File

@@ -0,0 +1,52 @@
CREATE TABLE IF NOT EXISTS guest_messages (
id SERIAL PRIMARY KEY,
guest_id VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
language VARCHAR(10) DEFAULT 'en',
is_ai BOOLEAN DEFAULT false,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_indexes
WHERE tablename = 'guest_messages' AND indexname = 'idx_guest_messages_guest_id'
) THEN
CREATE INDEX idx_guest_messages_guest_id ON guest_messages(guest_id);
END IF;
END $$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'messages' AND column_name = 'guest_message_id'
) THEN
ALTER TABLE messages ADD COLUMN guest_message_id INTEGER;
END IF;
END $$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_messages_guest_message'
) THEN
ALTER TABLE messages
ADD CONSTRAINT fk_messages_guest_message
FOREIGN KEY (guest_message_id)
REFERENCES guest_messages(id)
ON DELETE SET NULL;
END IF;
END $$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_indexes
WHERE tablename = 'messages' AND indexname = 'idx_messages_guest_message_id'
) THEN
CREATE INDEX idx_messages_guest_message_id ON messages(guest_message_id);
END IF;
END $$;

View File

@@ -0,0 +1,46 @@
CREATE TABLE IF NOT EXISTS user_preferences (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id),
preference_key VARCHAR(50) NOT NULL,
preference_value TEXT,
metadata JSONB DEFAULT '{}',
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
UNIQUE(user_id, preference_key)
);
-- Добавляем колонку metadata, если её нет
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'user_preferences' AND column_name = 'metadata'
) THEN
ALTER TABLE user_preferences ADD COLUMN metadata JSONB DEFAULT '{}';
END IF;
END $$;
CREATE INDEX IF NOT EXISTS idx_user_preferences_user_id ON user_preferences(user_id);
-- Базовые настройки
DO $$
BEGIN
INSERT INTO user_preferences (user_id, preference_key, preference_value, metadata)
SELECT id, 'language', 'ru', '{"available": ["ru", "en"]}'::jsonb
FROM users u
WHERE NOT EXISTS (
SELECT 1 FROM user_preferences
WHERE preference_key = 'language' AND user_id = u.id
);
END $$;
DO $$
BEGIN
INSERT INTO user_preferences (user_id, preference_key, preference_value, metadata)
SELECT id, 'notifications', 'true', '{"channels": ["email", "telegram"]}'::jsonb
FROM users u
WHERE NOT EXISTS (
SELECT 1 FROM user_preferences
WHERE preference_key = 'notifications' AND user_id = u.id
);
END $$;

View File

@@ -0,0 +1,45 @@
-- Добавляем новые поля в messages
ALTER TABLE messages
ADD COLUMN IF NOT EXISTS role VARCHAR(20) NOT NULL DEFAULT 'user',
ADD COLUMN IF NOT EXISTS guest_message_id INTEGER,
ADD COLUMN IF NOT EXISTS tokens_used INTEGER DEFAULT 0,
ADD COLUMN IF NOT EXISTS is_processed BOOLEAN DEFAULT FALSE;
-- Создаем функцию для связывания сообщений
CREATE OR REPLACE FUNCTION link_guest_messages(
p_user_id INTEGER,
p_guest_id VARCHAR(255)
) RETURNS VOID AS $$
DECLARE
v_conversation_id INTEGER;
BEGIN
-- Создаем новую беседу для гостевых сообщений
INSERT INTO conversations (created_at, updated_at)
VALUES (NOW(), NOW())
RETURNING id INTO v_conversation_id;
-- Копируем гостевые сообщения в основную таблицу
INSERT INTO messages (
conversation_id,
sender_type,
sender_id,
content,
role,
channel,
guest_message_id,
created_at
)
SELECT
v_conversation_id,
CASE WHEN is_ai THEN 'assistant' ELSE 'user' END,
CASE WHEN NOT is_ai THEN p_user_id ELSE NULL END,
content,
CASE WHEN is_ai THEN 'assistant' ELSE 'user' END,
'chat',
id,
created_at
FROM guest_messages
WHERE guest_id = p_guest_id
ORDER BY created_at;
END;
$$ LANGUAGE plpgsql;

View File

@@ -0,0 +1,13 @@
CREATE TABLE IF NOT EXISTS nonces (
id SERIAL PRIMARY KEY,
identity_value VARCHAR(255) NOT NULL,
nonce VARCHAR(255) NOT NULL,
expires_at TIMESTAMP NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
-- Индекс для быстрого поиска по identity_value
CREATE INDEX IF NOT EXISTS idx_nonces_identity_value ON nonces(identity_value);
-- Индекс для очистки просроченных nonce
CREATE INDEX IF NOT EXISTS idx_nonces_expires_at ON nonces(expires_at);

View File

@@ -0,0 +1,71 @@
-- Проверяем существование типа user_role
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'user_role') THEN
CREATE TYPE user_role AS ENUM ('user', 'admin');
END IF;
END $$;
-- Удаляем лишние колонки и связи
ALTER TABLE users DROP CONSTRAINT IF EXISTS users_role_id_fkey;
ALTER TABLE users DROP COLUMN IF EXISTS role_id;
-- Добавляем колонку role если её нет
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'users' AND column_name = 'role'
) THEN
ALTER TABLE users ADD COLUMN role user_role DEFAULT 'user'::user_role;
END IF;
END $$;
-- Удаляем лишние триггеры и функции
DROP TRIGGER IF EXISTS sync_identity_type_trigger ON user_identities;
DROP TRIGGER IF EXISTS user_identity_role_check ON user_identities;
DROP TRIGGER IF EXISTS check_admin_role_trigger ON user_identities;
DROP FUNCTION IF EXISTS sync_identity_type() CASCADE;
DROP FUNCTION IF EXISTS update_user_role() CASCADE;
DROP FUNCTION IF EXISTS check_admin_role(INTEGER) CASCADE;
-- Создаем функцию проверки роли
CREATE FUNCTION check_admin_role()
RETURNS TRIGGER AS $$
DECLARE
v_wallet_address VARCHAR;
BEGIN
SELECT provider_id INTO v_wallet_address
FROM user_identities
WHERE user_id = NEW.user_id
AND provider = 'wallet'
LIMIT 1;
IF v_wallet_address IS NULL THEN
RETURN NEW;
END IF;
UPDATE users
SET role = 'admin'::user_role
WHERE id = NEW.user_id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Создаем триггер
CREATE TRIGGER check_admin_role_trigger
AFTER INSERT OR UPDATE ON user_identities
FOR EACH ROW
EXECUTE FUNCTION check_admin_role();
-- Обновляем существующие записи
UPDATE users u
SET role = CASE
WHEN EXISTS (
SELECT 1 FROM user_identities ui
WHERE ui.user_id = u.id
AND ui.provider = 'wallet'
) THEN 'admin'::user_role
ELSE 'user'::user_role
END;

View File

@@ -1,15 +0,0 @@
// Создаем таблицу для хранения сообщений неаутентифицированных пользователей
const createGuestMessagesTable = `
CREATE TABLE IF NOT EXISTS guest_messages (
id SERIAL PRIMARY KEY,
guest_id VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
language VARCHAR(10) DEFAULT 'en',
is_ai BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_guest_messages_guest_id ON guest_messages(guest_id);
`;
module.exports = createGuestMessagesTable;

View File

@@ -0,0 +1,56 @@
CREATE OR REPLACE FUNCTION link_guest_messages(
p_user_id INTEGER,
p_guest_id VARCHAR(255)
) RETURNS VOID AS $$
DECLARE
v_conversation_id INTEGER;
v_count INTEGER;
BEGIN
-- Логируем входные параметры
RAISE NOTICE 'Linking messages for user_id: %, guest_id: %', p_user_id, p_guest_id;
-- Проверяем наличие гостевых сообщений
SELECT COUNT(*) INTO v_count
FROM guest_messages
WHERE guest_id = p_guest_id;
RAISE NOTICE 'Found % guest messages', v_count;
-- Создаем новую беседу
INSERT INTO conversations (user_id, created_at, updated_at)
VALUES (p_user_id, NOW(), NOW())
RETURNING id INTO v_conversation_id;
RAISE NOTICE 'Created conversation with id: %', v_conversation_id;
-- Копируем сообщения пользователя
WITH inserted_messages AS (
INSERT INTO messages (
conversation_id,
sender_type,
sender_id,
content,
role,
channel,
guest_message_id,
created_at
)
SELECT
v_conversation_id,
CASE WHEN is_ai THEN 'assistant' ELSE 'user' END,
CASE WHEN NOT is_ai THEN p_user_id ELSE NULL END,
content,
CASE WHEN is_ai THEN 'assistant' ELSE 'user' END,
'chat',
id, -- Сохраняем связь с гостевым сообщением
created_at
FROM guest_messages
WHERE guest_id = p_guest_id
ORDER BY created_at
RETURNING id
)
SELECT COUNT(*) INTO v_count FROM inserted_messages;
RAISE NOTICE 'Inserted % messages', v_count;
END;
$$ LANGUAGE plpgsql;