Files
minicrm/test-bd.js
2026-02-19 21:39:17 +05:00

603 lines
29 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// test-bd.js - Универсальная проверка и исправление структуры базы данных
const sqlite3 = require('sqlite3').verbose();
const path = require('path');
const fs = require('fs');
// Цвета для вывода в консоль
const colors = {
reset: '\x1b[0m',
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
magenta: '\x1b[35m',
cyan: '\x1b[36m'
};
console.log(`${colors.cyan}🔍 ЗАПУСК ПРОВЕРКИ БАЗЫ ДАННЫХ${colors.reset}`);
console.log('=' .repeat(60));
// Путь к базе данных
const dbPath = path.join(__dirname, 'data', 'school_crm.db');
console.log(`${colors.blue}📁 База данных:${colors.reset} ${dbPath}`);
// Проверяем существование файла базы данных
if (!fs.existsSync(dbPath)) {
console.log(`${colors.yellow}⚠️ Файл базы данных не найден. Он будет создан при первом запуске.${colors.reset}`);
} else {
const stats = fs.statSync(dbPath);
console.log(`${colors.green}✅ Файл базы данных существует (${(stats.size / 1024).toFixed(2)} KB)${colors.reset}`);
}
// Подключаемся к базе данных
const db = new sqlite3.Database(dbPath);
// Определяем ожидаемую структуру всех таблиц
const expectedTables = {
// Основные таблицы пользователей
users: {
columns: [
{ name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
{ name: 'login', type: 'TEXT UNIQUE NOT NULL' },
{ name: 'password', type: 'TEXT' },
{ name: 'name', type: 'TEXT NOT NULL' },
{ name: 'email', type: 'TEXT UNIQUE NOT NULL' },
{ name: 'role', type: 'TEXT DEFAULT "teacher"' },
{ name: 'auth_type', type: 'TEXT DEFAULT "local"' },
{ name: 'groups', type: 'TEXT' },
{ name: 'description', type: 'TEXT' },
{ name: 'avatar', type: 'TEXT' },
{ name: 'created_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' },
{ name: 'last_login', type: 'DATETIME' },
{ name: 'updated_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' }
],
indexes: [
'CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)',
'CREATE INDEX IF NOT EXISTS idx_users_role ON users(role)',
'CREATE INDEX IF NOT EXISTS idx_users_login ON users(login)'
]
},
// Таблица задач
tasks: {
columns: [
{ name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
{ name: 'title', type: 'TEXT NOT NULL' },
{ name: 'description', type: 'TEXT' },
{ name: 'status', type: 'TEXT DEFAULT "active"' },
{ name: 'created_by', type: 'INTEGER NOT NULL' },
{ name: 'created_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' },
{ name: 'updated_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' },
{ name: 'deleted_at', type: 'DATETIME' },
{ name: 'deleted_by', type: 'INTEGER' },
{ name: 'original_task_id', type: 'INTEGER' },
{ name: 'start_date', type: 'DATETIME' },
{ name: 'due_date', type: 'DATETIME' },
{ name: 'rework_comment', type: 'TEXT' },
{ name: 'closed_at', type: 'DATETIME' },
{ name: 'closed_by', type: 'INTEGER' },
{ name: 'task_type', type: 'TEXT DEFAULT "regular"' },
{ name: 'type', type: 'TEXT' },
{ name: 'approver_group_id', type: 'INTEGER' },
{ name: 'document_id', type: 'INTEGER' }
],
indexes: [
'CREATE INDEX IF NOT EXISTS idx_tasks_created_by ON tasks(created_by)',
'CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status)',
'CREATE INDEX IF NOT EXISTS idx_tasks_task_type ON tasks(task_type)',
'CREATE INDEX IF NOT EXISTS idx_tasks_due_date ON tasks(due_date)',
'CREATE INDEX IF NOT EXISTS idx_tasks_closed_at ON tasks(closed_at)',
'CREATE INDEX IF NOT EXISTS idx_tasks_original_task_id ON tasks(original_task_id)'
]
},
// Назначения задач
task_assignments: {
columns: [
{ name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
{ name: 'task_id', type: 'INTEGER NOT NULL' },
{ name: 'user_id', type: 'INTEGER NOT NULL' },
{ name: 'status', type: 'TEXT DEFAULT "assigned"' },
{ name: 'start_date', type: 'DATETIME' },
{ name: 'due_date', type: 'DATETIME' },
{ name: 'rework_comment', type: 'TEXT' },
{ name: 'created_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' },
{ name: 'updated_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' }
],
indexes: [
'CREATE INDEX IF NOT EXISTS idx_task_assignments_task_id ON task_assignments(task_id)',
'CREATE INDEX IF NOT EXISTS idx_task_assignments_user_id ON task_assignments(user_id)',
'CREATE INDEX IF NOT EXISTS idx_task_assignments_status ON task_assignments(status)',
'CREATE UNIQUE INDEX IF NOT EXISTS idx_task_assignments_unique ON task_assignments(task_id, user_id) WHERE status != "deleted"'
]
},
// Файлы задач
task_files: {
columns: [
{ name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
{ name: 'task_id', type: 'INTEGER NOT NULL' },
{ name: 'user_id', type: 'INTEGER NOT NULL' },
{ name: 'filename', type: 'TEXT NOT NULL' },
{ name: 'original_name', type: 'TEXT NOT NULL' },
{ name: 'file_path', type: 'TEXT NOT NULL' },
{ name: 'file_size', type: 'INTEGER NOT NULL' },
{ name: 'uploaded_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' }
],
indexes: [
'CREATE INDEX IF NOT EXISTS idx_task_files_task_id ON task_files(task_id)',
'CREATE INDEX IF NOT EXISTS idx_task_files_user_id ON task_files(user_id)'
]
},
// Логи активности
activity_logs: {
columns: [
{ name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
{ name: 'task_id', type: 'INTEGER NOT NULL' },
{ name: 'user_id', type: 'INTEGER NOT NULL' },
{ name: 'action', type: 'TEXT NOT NULL' },
{ name: 'details', type: 'TEXT' },
{ name: 'created_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' }
],
indexes: [
'CREATE INDEX IF NOT EXISTS idx_activity_logs_task_id ON activity_logs(task_id)',
'CREATE INDEX IF NOT EXISTS idx_activity_logs_created_at ON activity_logs(created_at)'
]
},
// Группы пользователей
user_groups: {
columns: [
{ name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
{ name: 'name', type: 'TEXT NOT NULL UNIQUE' },
{ name: 'description', type: 'TEXT' },
{ name: 'color', type: 'TEXT DEFAULT "#3498db"' },
{ name: 'can_approve_documents', type: 'BOOLEAN DEFAULT 0' },
{ name: 'created_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' },
{ name: 'updated_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' }
],
indexes: [
'CREATE INDEX IF NOT EXISTS idx_user_groups_name ON user_groups(name)',
'CREATE INDEX IF NOT EXISTS idx_user_groups_can_approve ON user_groups(can_approve_documents)'
]
},
// Членство в группах
user_group_memberships: {
columns: [
{ name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
{ name: 'user_id', type: 'INTEGER NOT NULL' },
{ name: 'group_id', type: 'INTEGER NOT NULL' },
{ name: 'created_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' }
],
indexes: [
'CREATE INDEX IF NOT EXISTS idx_user_group_memberships_user_id ON user_group_memberships(user_id)',
'CREATE INDEX IF NOT EXISTS idx_user_group_memberships_group_id ON user_group_memberships(group_id)',
'CREATE UNIQUE INDEX IF NOT EXISTS idx_user_group_memberships_unique ON user_group_memberships(user_id, group_id)'
]
},
// Типы документов (простые)
simple_document_types: {
columns: [
{ name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
{ name: 'name', type: 'TEXT NOT NULL' },
{ name: 'description', type: 'TEXT' },
{ name: 'created_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' }
],
indexes: [
'CREATE INDEX IF NOT EXISTS idx_simple_document_types_name ON simple_document_types(name)'
]
},
// Документы (простые)
simple_documents: {
columns: [
{ name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
{ name: 'task_id', type: 'INTEGER NOT NULL' },
{ name: 'document_type_id', type: 'INTEGER' },
{ name: 'document_number', type: 'TEXT' },
{ name: 'document_date', type: 'DATE' },
{ name: 'pages_count', type: 'INTEGER' },
{ name: 'urgency_level', type: 'TEXT CHECK(urgency_level IN ("normal", "urgent", "very_urgent"))' },
{ name: 'comment', type: 'TEXT' },
{ name: 'refusal_reason', type: 'TEXT' }
],
indexes: [
'CREATE INDEX IF NOT EXISTS idx_simple_documents_task_id ON simple_documents(task_id)',
'CREATE INDEX IF NOT EXISTS idx_simple_documents_document_number ON simple_documents(document_number)'
]
},
// Настройки пользователей
user_settings: {
columns: [
{ name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
{ name: 'user_id', type: 'INTEGER UNIQUE NOT NULL' },
{ name: 'email_notifications', type: 'BOOLEAN DEFAULT 1' },
{ name: 'notification_email', type: 'TEXT' },
{ name: 'telegram_notifications', type: 'BOOLEAN DEFAULT 0' },
{ name: 'telegram_chat_id', type: 'TEXT' },
{ name: 'vk_notifications', type: 'BOOLEAN DEFAULT 0' },
{ name: 'vk_user_id', type: 'TEXT' },
{ name: 'created_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' },
{ name: 'updated_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' }
],
indexes: [
'CREATE INDEX IF NOT EXISTS idx_user_settings_user_id ON user_settings(user_id)'
]
},
// История уведомлений
notification_history: {
columns: [
{ name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
{ name: 'user_id', type: 'INTEGER NOT NULL' },
{ name: 'task_id', type: 'INTEGER NOT NULL' },
{ name: 'notification_type', type: 'TEXT NOT NULL' },
{ name: 'last_sent_at', type: 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP' },
{ name: 'created_at', type: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP' }
],
indexes: [
'CREATE INDEX IF NOT EXISTS idx_notification_history_user_id ON notification_history(user_id)',
'CREATE INDEX IF NOT EXISTS idx_notification_history_task_id ON notification_history(task_id)',
'CREATE INDEX IF NOT EXISTS idx_notification_history_type ON notification_history(notification_type)',
'CREATE UNIQUE INDEX IF NOT EXISTS idx_notification_history_unique ON notification_history(user_id, task_id, notification_type)'
]
},
// Очередь email
email_queue: {
columns: [
{ name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
{ name: 'to_email', type: 'TEXT NOT NULL' },
{ name: 'subject', type: 'TEXT NOT NULL' },
{ name: 'html_content', type: 'TEXT NOT NULL' },
{ name: 'user_id', type: 'INTEGER' },
{ name: 'task_id', type: 'INTEGER' },
{ name: 'notification_type', type: 'TEXT' },
{ name: 'retry_count', type: 'INTEGER DEFAULT 0' },
{ name: 'status', type: 'TEXT DEFAULT "pending"' },
{ name: 'error_message', type: 'TEXT' },
{ name: 'created_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' },
{ name: 'updated_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' }
],
indexes: [
'CREATE INDEX IF NOT EXISTS idx_email_queue_status ON email_queue(status, created_at)',
'CREATE INDEX IF NOT EXISTS idx_email_queue_user_id ON email_queue(user_id)',
'CREATE INDEX IF NOT EXISTS idx_email_queue_task_id ON email_queue(task_id)'
]
},
// ===== НОВЫЕ ТАБЛИЦЫ ДЛЯ ЧАТА =====
// Сообщения чата задач
task_chat_messages: {
columns: [
{ name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
{ name: 'task_id', type: 'INTEGER NOT NULL' },
{ name: 'user_id', type: 'INTEGER NOT NULL' },
{ name: 'message', type: 'TEXT NOT NULL' },
{ name: 'created_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' },
{ name: 'updated_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' },
{ name: 'is_edited', type: 'BOOLEAN DEFAULT 0' },
{ name: 'is_deleted', type: 'BOOLEAN DEFAULT 0' },
{ name: 'reply_to_id', type: 'INTEGER' }
],
foreign_keys: [
'FOREIGN KEY (task_id) REFERENCES tasks (id) ON DELETE CASCADE',
'FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE',
'FOREIGN KEY (reply_to_id) REFERENCES task_chat_messages (id) ON DELETE SET NULL'
],
indexes: [
'CREATE INDEX IF NOT EXISTS idx_task_chat_messages_task_id ON task_chat_messages(task_id)',
'CREATE INDEX IF NOT EXISTS idx_task_chat_messages_user_id ON task_chat_messages(user_id)',
'CREATE INDEX IF NOT EXISTS idx_task_chat_messages_created_at ON task_chat_messages(created_at)',
'CREATE INDEX IF NOT EXISTS idx_task_chat_messages_reply_to ON task_chat_messages(reply_to_id)'
]
},
// Файлы в сообщениях чата
task_chat_files: {
columns: [
{ name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
{ name: 'message_id', type: 'INTEGER NOT NULL' },
{ name: 'file_path', type: 'TEXT NOT NULL' },
{ name: 'original_name', type: 'TEXT NOT NULL' },
{ name: 'file_size', type: 'INTEGER NOT NULL' },
{ name: 'file_type', type: 'TEXT' },
{ name: 'uploaded_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' }
],
foreign_keys: [
'FOREIGN KEY (message_id) REFERENCES task_chat_messages (id) ON DELETE CASCADE'
],
indexes: [
'CREATE INDEX IF NOT EXISTS idx_task_chat_files_message_id ON task_chat_files(message_id)'
]
},
// Прочитанные сообщения
task_chat_reads: {
columns: [
{ name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
{ name: 'message_id', type: 'INTEGER NOT NULL' },
{ name: 'user_id', type: 'INTEGER NOT NULL' },
{ name: 'read_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' }
],
foreign_keys: [
'FOREIGN KEY (message_id) REFERENCES task_chat_messages (id) ON DELETE CASCADE',
'FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE'
],
indexes: [
'CREATE INDEX IF NOT EXISTS idx_task_chat_reads_message_id ON task_chat_reads(message_id)',
'CREATE INDEX IF NOT EXISTS idx_task_chat_reads_user_id ON task_chat_reads(user_id)',
'CREATE UNIQUE INDEX IF NOT EXISTS idx_task_chat_reads_unique ON task_chat_reads(message_id, user_id)'
]
}
};
// Функция для получения списка существующих таблиц
function getExistingTables() {
return new Promise((resolve, reject) => {
db.all("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name", [], (err, rows) => {
if (err) {
reject(err);
} else {
resolve(rows.map(row => row.name));
}
});
});
}
// Функция для получения структуры таблицы
function getTableInfo(tableName) {
return new Promise((resolve, reject) => {
db.all(`PRAGMA table_info(${tableName})`, [], (err, rows) => {
if (err) {
reject(err);
} else {
resolve(rows);
}
});
});
}
// Функция для получения индексов таблицы
function getTableIndexes(tableName) {
return new Promise((resolve, reject) => {
db.all(`PRAGMA index_list(${tableName})`, [], (err, rows) => {
if (err) {
reject(err);
} else {
resolve(rows);
}
});
});
}
// Функция для добавления колонки
function addColumn(tableName, columnName, columnType) {
return new Promise((resolve, reject) => {
console.log(`${colors.yellow} Добавление колонки: ${columnName} (${columnType})${colors.reset}`);
db.run(`ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${columnType}`, function(err) {
if (err) {
console.log(`${colors.red} ❌ Ошибка: ${err.message}${colors.reset}`);
reject(err);
} else {
console.log(`${colors.green} ✅ Колонка добавлена${colors.reset}`);
resolve();
}
});
});
}
// Функция для создания таблицы
function createTable(tableName, tableDefinition) {
return new Promise((resolve, reject) => {
console.log(`${colors.yellow} 🏗️ Создание таблицы: ${tableName}${colors.reset}`);
let createSQL = `CREATE TABLE IF NOT EXISTS ${tableName} (\n`;
// Добавляем колонки
const columnDefs = tableDefinition.columns.map(col => ` ${col.name} ${col.type}`).join(',\n');
createSQL += columnDefs;
// Добавляем внешние ключи, если есть
if (tableDefinition.foreign_keys && tableDefinition.foreign_keys.length > 0) {
createSQL += ',\n' + tableDefinition.foreign_keys.map(fk => ` ${fk}`).join(',\n');
}
createSQL += '\n)';
db.run(createSQL, function(err) {
if (err) {
console.log(`${colors.red} ❌ Ошибка: ${err.message}${colors.reset}`);
reject(err);
} else {
console.log(`${colors.green} ✅ Таблица создана${colors.reset}`);
resolve();
}
});
});
}
// Функция для создания индекса
function createIndex(indexSQL) {
return new Promise((resolve, reject) => {
db.run(indexSQL, function(err) {
if (err) {
// Игнорируем ошибки создания индексов, так как они могут уже существовать
resolve();
} else {
resolve();
}
});
});
}
// Основная функция проверки
async function checkDatabase() {
console.log(`${colors.cyan}🔍 Получение списка существующих таблиц...${colors.reset}`);
try {
const existingTables = await getExistingTables();
console.log(`${colors.green}✅ Найдено таблиц: ${existingTables.length}${colors.reset}`);
// Проверяем наличие всех ожидаемых таблиц
const expectedTableNames = Object.keys(expectedTables);
const missingTables = expectedTableNames.filter(t => !existingTables.includes(t));
const extraTables = existingTables.filter(t => !expectedTableNames.includes(t) && !t.startsWith('sqlite_'));
console.log(`\n${colors.cyan}📊 СТАТИСТИКА ТАБЛИЦ:${colors.reset}`);
console.log(` Ожидаемых таблиц: ${expectedTableNames.length}`);
console.log(` Существующих таблиц: ${existingTables.length}`);
console.log(` Отсутствует таблиц: ${missingTables.length}`);
console.log(` Лишних таблиц: ${extraTables.length}`);
if (extraTables.length > 0) {
console.log(`\n${colors.yellow}⚠️ Лишние таблицы (не требуются, но можно оставить):${colors.reset}`);
extraTables.forEach(t => console.log(` - ${t}`));
}
// Проверяем структуру каждой ожидаемой таблицы
console.log(`\n${colors.cyan}🔧 ПРОВЕРКА СТРУКТУРЫ ТАБЛИЦ:${colors.reset}`);
for (const tableName of expectedTableNames) {
console.log(`\n${colors.magenta}📋 Таблица: ${tableName}${colors.reset}`);
const tableDef = expectedTables[tableName];
if (!existingTables.includes(tableName)) {
// Таблица не существует - создаём
console.log(`${colors.yellow} ⚠️ Таблица не существует${colors.reset}`);
await createTable(tableName, tableDef);
// Создаём индексы для новой таблицы
if (tableDef.indexes && tableDef.indexes.length > 0) {
console.log(`${colors.yellow} 🔧 Создание индексов...${colors.reset}`);
for (const indexSQL of tableDef.indexes) {
await createIndex(indexSQL);
}
console.log(`${colors.green} ✅ Индексы созданы${colors.reset}`);
}
continue;
}
// Таблица существует - проверяем колонки
const columns = await getTableInfo(tableName);
const existingColumnNames = columns.map(c => c.name.toLowerCase());
console.log(` 📊 Колонок в БД: ${columns.length}, требуется: ${tableDef.columns.length}`);
// Проверяем наличие всех необходимых колонок
for (const expectedCol of tableDef.columns) {
const colName = expectedCol.name.toLowerCase();
if (!existingColumnNames.includes(colName)) {
console.log(`${colors.yellow} ⚠️ Отсутствует колонка: ${expectedCol.name}${colors.reset}`);
await addColumn(tableName, expectedCol.name, expectedCol.type);
}
}
// Проверяем типы данных колонок (базовая проверка)
for (const existingCol of columns) {
const expectedCol = tableDef.columns.find(c => c.name.toLowerCase() === existingCol.name.toLowerCase());
if (expectedCol) {
const expectedType = expectedCol.type.split(' ')[0].toUpperCase();
const existingType = existingCol.type.toUpperCase();
if (!existingType.includes(expectedType) && !expectedType.includes(existingType)) {
console.log(`${colors.yellow} ⚠️ Несоответствие типа: ${existingCol.name} - ожидается ${expectedType}, в БД ${existingType}${colors.reset}`);
console.log(` Ручное изменение типа данных может привести к потере данных. Пропускаем.`);
}
}
}
// Проверяем индексы
try {
const indexes = await getTableIndexes(tableName);
const existingIndexNames = indexes.map(i => i.name.toLowerCase());
if (tableDef.indexes && tableDef.indexes.length > 0) {
console.log(` 🔍 Проверка индексов...`);
for (const indexSQL of tableDef.indexes) {
// Извлекаем имя индекса из SQL (упрощённо)
const match = indexSQL.match(/INDEX\s+IF NOT EXISTS\s+(\w+)/i) ||
indexSQL.match(/INDEX\s+(\w+)/i);
if (match) {
const indexName = match[1].toLowerCase();
if (!existingIndexNames.includes(indexName)) {
console.log(`${colors.yellow} Создание индекса: ${indexName}${colors.reset}`);
await createIndex(indexSQL);
}
}
}
}
} catch (err) {
console.log(`${colors.red} ❌ Ошибка проверки индексов: ${err.message}${colors.reset}`);
}
// Проверяем внешние ключи (только для SQLite - ограниченная поддержка)
if (tableDef.foreign_keys && tableDef.foreign_keys.length > 0) {
// В SQLite сложно проверить внешние ключи через PRAGMA, просто удостоверимся что таблица создана правильно
console.log(` 🔍 Внешние ключи определены в структуре таблицы`);
}
}
// Проверяем наличие директорий для файлов
console.log(`\n${colors.cyan}📁 ПРОВЕРКА ДИРЕКТОРИЙ:${colors.reset}`);
const dirsToCheck = [
path.join(__dirname, 'data', 'uploads'),
path.join(__dirname, 'data', 'uploads', 'tasks'),
path.join(__dirname, 'data', 'uploads', 'chat'),
path.join(__dirname, 'data', 'logs')
];
dirsToCheck.forEach(dir => {
if (!fs.existsSync(dir)) {
console.log(`${colors.yellow} 📁 Создание директории: ${path.basename(dir)}${colors.reset}`);
fs.mkdirSync(dir, { recursive: true });
console.log(`${colors.green} ✅ Создано${colors.reset}`);
} else {
console.log(`${colors.green} ✅ Директория существует: ${path.basename(dir)}${colors.reset}`);
}
});
// Итоговый отчёт
console.log(`\n${colors.cyan}🏁 ИТОГОВЫЙ ОТЧЁТ:${colors.reset}`);
console.log('=' .repeat(60));
// Проверяем все ли таблицы теперь существуют
const finalTables = await getExistingTables();
const stillMissing = expectedTableNames.filter(t => !finalTables.includes(t));
if (stillMissing.length === 0) {
console.log(`${colors.green}Все необходимые таблицы присутствуют в базе данных.${colors.reset}`);
} else {
console.log(`${colors.red}❌ Отсутствуют таблицы: ${stillMissing.join(', ')}${colors.reset}`);
}
console.log(`\n${colors.green}✨ Проверка базы данных завершена!${colors.reset}`);
console.log('=' .repeat(60));
} catch (error) {
console.error(`${colors.red}❌ Критическая ошибка:${colors.reset}`, error);
} finally {
// Закрываем соединение с базой данных
db.close((err) => {
if (err) {
console.error(`${colors.red}❌ Ошибка закрытия БД:${colors.reset}`, err.message);
} else {
console.log(`${colors.green}✅ Соединение с БД закрыто${colors.reset}`);
}
});
}
}
// Запускаем проверку
checkDatabase();