email and fix

This commit is contained in:
2026-01-26 17:44:28 +05:00
parent 4985b4727b
commit 77122aa9ee
16 changed files with 3531 additions and 2330 deletions

View File

@@ -174,9 +174,176 @@ function createSQLiteTables() {
notification_key TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`);
console.log('✅ База данных SQLite инициализирована');
setTimeout(addMissingColumns, 1000);
// Добавляем таблицу для пользовательских настроек
db.run(`CREATE TABLE IF NOT EXISTS user_settings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER UNIQUE NOT NULL,
email_notifications BOOLEAN DEFAULT true,
notification_email TEXT,
telegram_notifications BOOLEAN DEFAULT false,
telegram_chat_id TEXT,
vk_notifications BOOLEAN DEFAULT false,
vk_user_id TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id)
)`);
console.log('✅ Таблица для пользовательских настроек инициализирована');
// Запускаем проверку и обновление структуры таблиц
setTimeout(() => {
checkAndUpdateTableStructure();
}, 2000);
}
// Функция для проверки и обновления структуры таблиц
function checkAndUpdateTableStructure() {
console.log('🔍 Проверка структуры таблиц...');
// Определяем ожидаемую структуру таблиц
const tableSchemas = {
users: [
{ 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: 'created_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' },
{ name: 'last_login', type: 'DATETIME' },
{ name: 'updated_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' }
],
tasks: [
{ 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' }
],
task_assignments: [
{ 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' }
],
task_files: [
{ 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' }
],
activity_logs: [
{ 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' }
],
notification_logs: [
{ name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
{ name: 'notification_key', type: 'TEXT NOT NULL' },
{ name: 'created_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' }
],
user_settings: [
{ name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
{ name: 'user_id', type: 'INTEGER UNIQUE NOT NULL' },
{ name: 'email_notifications', type: 'BOOLEAN DEFAULT true' },
{ name: 'notification_email', type: 'TEXT' },
{ name: 'telegram_notifications', type: 'BOOLEAN DEFAULT false' },
{ name: 'telegram_chat_id', type: 'TEXT' },
{ name: 'vk_notifications', type: 'BOOLEAN DEFAULT false' },
{ name: 'vk_user_id', type: 'TEXT' },
{ name: 'created_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' },
{ name: 'updated_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' }
]
};
// Проверяем каждую таблицу
Object.entries(tableSchemas).forEach(([tableName, columns]) => {
db.all(`PRAGMA table_info(${tableName})`, (err, existingColumns) => {
if (err) {
console.error(`❌ Ошибка проверки таблицы ${tableName}:`, err.message);
return;
}
if (existingColumns.length === 0) {
console.log(`⚠️ Таблица ${tableName} не существует, создаем...`);
// Таблица будет создана автоматически при следующем запуске
return;
}
// Создаем массив имен существующих колонок
const existingColumnNames = existingColumns.map(col => col.name.toLowerCase());
// Проверяем каждую ожидаемую колонку
columns.forEach(expectedColumn => {
const expectedName = expectedColumn.name.toLowerCase();
if (!existingColumnNames.includes(expectedName)) {
console.log(`🔧 Добавляем колонку ${expectedColumn.name} в таблицу ${tableName}...`);
db.run(
`ALTER TABLE ${tableName} ADD COLUMN ${expectedColumn.name} ${expectedColumn.type}`,
(alterErr) => {
if (alterErr) {
console.error(`❌ Ошибка добавления колонки ${expectedColumn.name}:`, alterErr.message);
} else {
console.log(`✅ Колонка ${expectedColumn.name} добавлена в таблицу ${tableName}`);
}
}
);
}
});
});
});
// Проверяем индекс для таблицы user_settings
setTimeout(() => {
db.get("SELECT name FROM sqlite_master WHERE type='index' AND name='idx_user_settings_user_id'", (err, index) => {
if (err) {
console.error('❌ Ошибка проверки индекса:', err.message);
return;
}
if (!index) {
console.log('🔧 Создаем индекс для таблицы user_settings...');
db.run("CREATE INDEX IF NOT EXISTS idx_user_settings_user_id ON user_settings(user_id)", (createErr) => {
if (createErr) {
console.error('❌ Ошибка создания индекса:', createErr.message);
} else {
console.log('✅ Индекс для user_settings создан');
}
});
}
});
}, 1000);
}
function createPostgresAdapter(pool) {
@@ -400,6 +567,22 @@ async function createPostgresTables() {
)
`);
// Добавляем таблицу для пользовательских настроек
await client.query(`
CREATE TABLE IF NOT EXISTS user_settings (
id SERIAL PRIMARY KEY,
user_id INTEGER UNIQUE NOT NULL REFERENCES users(id),
email_notifications BOOLEAN DEFAULT true,
notification_email TEXT,
telegram_notifications BOOLEAN DEFAULT false,
telegram_chat_id TEXT,
vk_notifications BOOLEAN DEFAULT false,
vk_user_id TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`);
// Создаем индексы
const indexes = [
'CREATE INDEX IF NOT EXISTS idx_tasks_created_by ON tasks(created_by)',
@@ -410,7 +593,8 @@ async function createPostgresTables() {
'CREATE INDEX IF NOT EXISTS idx_task_assignments_status ON task_assignments(status)',
'CREATE INDEX IF NOT EXISTS idx_task_files_task_id ON task_files(task_id)',
'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)'
'CREATE INDEX IF NOT EXISTS idx_activity_logs_created_at ON activity_logs(created_at)',
'CREATE INDEX IF NOT EXISTS idx_user_settings_user_id ON user_settings(user_id)'
];
for (const indexQuery of indexes) {
@@ -424,40 +608,91 @@ async function createPostgresTables() {
client.release();
console.log('✅ Таблицы PostgreSQL проверены/созданы');
// Проверяем структуру PostgreSQL таблиц
await checkPostgresTableStructure();
} catch (error) {
console.error('❌ Ошибка создания таблиц PostgreSQL:', error.message);
}
}
function addMissingColumns() {
const columnsToAdd = [
{ table: 'tasks', column: 'rework_comment', type: 'TEXT' },
{ table: 'tasks', column: 'closed_at', type: 'DATETIME' },
{ table: 'tasks', column: 'closed_by', type: 'INTEGER' },
{ table: 'task_assignments', column: 'rework_comment', type: 'TEXT' }
];
columnsToAdd.forEach(({ table, column, type }) => {
db.all(`PRAGMA table_info(${table})`, (err, rows) => {
if (err) {
console.error(`Ошибка при проверке таблицы ${table}:`, err);
return;
}
const columnExists = rows.some(row => row.name === column);
if (!columnExists) {
db.run(`ALTER TABLE ${table} ADD COLUMN ${column} ${type}`, (err) => {
if (err) {
console.error(`Ошибка при добавлении колонки ${column} в таблицу ${table}:`, err);
} else {
console.log(`✅ Добавлена колонка ${column} в таблицу ${table}`);
// Функция для проверки структуры таблиц PostgreSQL
async function checkPostgresTableStructure() {
if (!USE_POSTGRES) return;
try {
const client = await postgresPool.connect();
console.log('🔍 Проверка структуры таблиц PostgreSQL...');
// Определяем ожидаемую структуру таблиц PostgreSQL
const tableSchemas = {
user_settings: [
{ name: 'id', type: 'SERIAL PRIMARY KEY' },
{ name: 'user_id', type: 'INTEGER UNIQUE NOT NULL REFERENCES users(id)' },
{ name: 'email_notifications', type: 'BOOLEAN DEFAULT true' },
{ name: 'notification_email', type: 'TEXT' },
{ name: 'telegram_notifications', type: 'BOOLEAN DEFAULT false' },
{ name: 'telegram_chat_id', type: 'TEXT' },
{ name: 'vk_notifications', type: 'BOOLEAN DEFAULT false' },
{ name: 'vk_user_id', type: 'TEXT' },
{ name: 'created_at', type: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP' },
{ name: 'updated_at', type: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP' }
]
};
// Проверяем каждую таблицу
for (const [tableName, columns] of Object.entries(tableSchemas)) {
try {
// Проверяем существование таблицы
const tableExists = await client.query(
"SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = $1)",
[tableName]
);
if (!tableExists.rows[0].exists) {
console.log(`⚠️ Таблица ${tableName} не существует в PostgreSQL`);
continue;
}
// Получаем существующие колонки
const existingColumns = await client.query(`
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_name = $1
ORDER BY ordinal_position
`, [tableName]);
const existingColumnNames = existingColumns.rows.map(col => col.column_name.toLowerCase());
// Проверяем каждую ожидаемую колонку
for (const expectedColumn of columns) {
const expectedName = expectedColumn.name.toLowerCase();
if (!existingColumnNames.includes(expectedName)) {
console.log(`🔧 Добавляем колонку ${expectedColumn.name} в таблицу PostgreSQL ${tableName}...`);
try {
await client.query(
`ALTER TABLE ${tableName} ADD COLUMN ${expectedColumn.name} ${expectedColumn.type}`
);
console.log(`✅ Колонка ${expectedColumn.name} добавлена в таблицу PostgreSQL ${tableName}`);
} catch (alterErr) {
console.error(`❌ Ошибка добавления колонки ${expectedColumn.name}:`, alterErr.message);
}
}
});
} else {
console.log(`✅ Колонка ${column} уже существует в таблице ${table}`);
}
} catch (error) {
console.error(`❌ Ошибка проверки таблицы PostgreSQL ${tableName}:`, error.message);
}
});
});
}
client.release();
console.log('✅ Проверка структуры таблиц PostgreSQL завершена');
} catch (error) {
console.error('❌ Ошибка проверки структуры таблиц PostgreSQL:', error.message);
}
}
function createTaskFolder(taskId) {
@@ -598,7 +833,8 @@ module.exports = {
updateTaskMetadata,
checkTaskAccess,
USE_POSTGRES,
getDatabaseType: () => USE_POSTGRES ? 'PostgreSQL' : 'SQLite'
getDatabaseType: () => USE_POSTGRES ? 'PostgreSQL' : 'SQLite',
checkAndUpdateTableStructure // Экспортируем для ручного запуска
};
// Запускаем инициализацию при экспорте (но она завершится позже)