feat(chat): личный чат тьютор ↔ админ с вложениями

Диалоги 1:1 (один тред на тьютора), отдельный SSE-канал whatido:chat
вместо общей ленты notifications, вложения с белым списком расширений
и вставкой из буфера (Ctrl+V), очистка по chat_retention_days.
Таблицы в бэкап, chat_attachments добавлен в sweepOrphanedUploads.
This commit is contained in:
dev
2026-10-05 17:30:26 +03:00
parent 3836fc82ef
commit 235fd43af4
12 changed files with 1409 additions and 10 deletions
+36
View File
@@ -343,6 +343,42 @@ CREATE TABLE IF NOT EXISTS notification_reads (
CREATE INDEX IF NOT EXISTS idx_notification_reads_user ON notification_reads(user_id);
CREATE TABLE IF NOT EXISTS chat_threads (
id SERIAL PRIMARY KEY,
tutor_id INT NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE,
last_message_at TIMESTAMPTZ DEFAULT now(),
last_message_text VARCHAR(300),
tutor_unread INT NOT NULL DEFAULT 0,
admin_unread INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_chat_threads_last_message ON chat_threads(last_message_at DESC);
CREATE TABLE IF NOT EXISTS chat_messages (
id SERIAL PRIMARY KEY,
thread_id INT NOT NULL REFERENCES chat_threads(id) ON DELETE CASCADE,
sender_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
body TEXT NOT NULL DEFAULT '',
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_chat_messages_thread ON chat_messages(thread_id, id);
CREATE TABLE IF NOT EXISTS chat_attachments (
id SERIAL PRIMARY KEY,
message_id INT NOT NULL REFERENCES chat_messages(id) ON DELETE CASCADE,
token VARCHAR(64) UNIQUE NOT NULL,
path VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
size INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_chat_attachments_message ON chat_attachments(message_id);
INSERT INTO settings (key, value) VALUES ('chat_retention_days', '365') ON CONFLICT (key) DO NOTHING;
INSERT INTO settings (key, value) VALUES ('notify_enabled', 'true')
ON CONFLICT (key) DO NOTHING;
INSERT INTO settings (key, value) VALUES ('notify_retention_days', '30')
+36
View File
@@ -313,6 +313,42 @@ CREATE TABLE IF NOT EXISTS notification_reads (
CREATE INDEX IF NOT EXISTS idx_notification_reads_user ON notification_reads(user_id);
CREATE TABLE IF NOT EXISTS chat_threads (
id SERIAL PRIMARY KEY,
tutor_id INT NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE,
last_message_at TIMESTAMPTZ DEFAULT now(),
last_message_text VARCHAR(300),
tutor_unread INT NOT NULL DEFAULT 0,
admin_unread INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_chat_threads_last_message ON chat_threads(last_message_at DESC);
CREATE TABLE IF NOT EXISTS chat_messages (
id SERIAL PRIMARY KEY,
thread_id INT NOT NULL REFERENCES chat_threads(id) ON DELETE CASCADE,
sender_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
body TEXT NOT NULL DEFAULT '',
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_chat_messages_thread ON chat_messages(thread_id, id);
CREATE TABLE IF NOT EXISTS chat_attachments (
id SERIAL PRIMARY KEY,
message_id INT NOT NULL REFERENCES chat_messages(id) ON DELETE CASCADE,
token VARCHAR(64) UNIQUE NOT NULL,
path VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
size INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_chat_attachments_message ON chat_attachments(message_id);
INSERT INTO settings (key, value) VALUES ('chat_retention_days', '365') ON CONFLICT (key) DO NOTHING;
INSERT INTO settings (key, value) VALUES ('notify_enabled', 'true') ON CONFLICT (key) DO NOTHING;
INSERT INTO settings (key, value) VALUES ('notify_retention_days', '30') ON CONFLICT (key) DO NOTHING;
INSERT INTO settings (key, value) VALUES ('notify_entry_new', 'true') ON CONFLICT (key) DO NOTHING;