From 6e8b5231cf8aa111841c10e170be5cc646ca3a15 Mon Sep 17 00:00:00 2001
From: kilyabin <65072190+kilyabin@users.noreply.github.com>
Date: Thu, 5 Mar 2026 16:33:52 +0400
Subject: [PATCH] Revert last 3 commits
---
.env.production.example | 3 -
scripts/test-teachers-db.js | 98 -----------------
scripts/test-teachers-parser.js | 76 -------------
src/app/parser/schedule.ts | 50 +--------
src/app/parser/teachers-list.ts | 75 +++----------
src/pages/api/admin/settings.ts | 16 +--
src/pages/api/admin/teachers.ts | 69 +++---------
src/shared/data/database.ts | 170 ++++-------------------------
src/shared/data/teachers-loader.ts | 3 +-
systemd/kspguti-schedule.service | 1 -
10 files changed, 65 insertions(+), 496 deletions(-)
delete mode 100644 scripts/test-teachers-db.js
delete mode 100644 scripts/test-teachers-parser.js
diff --git a/.env.production.example b/.env.production.example
index 632998f..fd91b6d 100644
--- a/.env.production.example
+++ b/.env.production.example
@@ -1,8 +1,5 @@
# Production environment variables for KSPGUTI Schedule
-# Database directory (where schedule-app.db will be stored)
-DATABASE_DIR=/opt/kspguti-schedule
-
# Site URL - used for canonical links and sitemap (optional, defaults to https://schedule.itlxrd.space)
NEXT_PUBLIC_SITE_URL=https://schedule.itlxrd.space
diff --git a/scripts/test-teachers-db.js b/scripts/test-teachers-db.js
deleted file mode 100644
index 889e706..0000000
--- a/scripts/test-teachers-db.js
+++ /dev/null
@@ -1,98 +0,0 @@
-#!/usr/bin/env node
-
-/**
- * Скрипт для проверки базы данных преподавателей
- * Запуск: node scripts/test-teachers-db.js
- */
-
-const path = require('path');
-const fs = require('fs');
-
-// Определяем директорию базы данных
-function getDatabaseDir() {
- if (process.env.DATABASE_DIR) {
- return process.env.DATABASE_DIR;
- }
-
- const cwd = process.cwd();
- console.log(`Current working directory: ${cwd}`);
-
- if (cwd.includes('.next/standalone')) {
- const standaloneMatch = cwd.match(/^(.+?)\/\.next\/standalone/);
- if (standaloneMatch && standaloneMatch[1]) {
- return standaloneMatch[1];
- }
- return path.resolve(cwd, '..', '..');
- }
-
- if (fs.existsSync('/opt/kspguti-schedule')) {
- return '/opt/kspguti-schedule';
- }
-
- return cwd;
-}
-
-const DATABASE_DIR = getDatabaseDir();
-const DB_PATH = path.join(DATABASE_DIR, 'db', 'schedule-app.db');
-
-console.log(`Database directory: ${DATABASE_DIR}`);
-console.log(`Database path: ${DB_PATH}`);
-console.log(`Database exists: ${fs.existsSync(DB_PATH)}`);
-
-if (!fs.existsSync(DB_PATH)) {
- console.error('Database file does not exist!');
- process.exit(1);
-}
-
-// Проверяем права доступа
-try {
- fs.accessSync(DB_PATH, fs.constants.R_OK | fs.constants.W_OK);
- console.log('Database file is readable and writable');
-} catch (err) {
- console.error('Database file permissions error:', err.message);
- process.exit(1);
-}
-
-// Подключаемся к базе данных
-const Database = require('better-sqlite3');
-const db = new Database(DB_PATH);
-
-// Проверяем таблицу teachers
-console.log('\n=== Teachers Table ===');
-const teachersCount = db.prepare('SELECT COUNT(*) as count FROM teachers').get();
-console.log(`Total teachers in database: ${teachersCount.count}`);
-
-if (teachersCount.count > 0) {
- const teachers = db.prepare('SELECT id, parseId, name FROM teachers LIMIT 10').all();
- console.log('First 10 teachers:');
- teachers.forEach((t, i) => {
- console.log(` ${i + 1}. [${t.id}] ${t.name} (parseId: ${t.parseId})`);
- });
-} else {
- console.log('Teachers table is EMPTY!');
-}
-
-// Проверяем таблицу groups
-console.log('\n=== Groups Table ===');
-const groupsCount = db.prepare('SELECT COUNT(*) as count FROM groups').get();
-console.log(`Total groups in database: ${groupsCount.count}`);
-
-if (groupsCount.count > 0) {
- const groups = db.prepare('SELECT id, parseId, name, course FROM groups LIMIT 10').all();
- console.log('First 10 groups:');
- groups.forEach((g, i) => {
- console.log(` ${i + 1}. [${g.id}] ${g.name} (parseId: ${g.parseId}, course: ${g.course})`);
- });
-}
-
-// Проверяем таблицу settings
-console.log('\n=== Settings Table ===');
-const settings = db.prepare('SELECT value FROM settings WHERE key = ?').get('app');
-if (settings) {
- console.log('App settings:', settings.value);
-} else {
- console.log('No app settings found');
-}
-
-db.close();
-console.log('\nDone!');
diff --git a/scripts/test-teachers-parser.js b/scripts/test-teachers-parser.js
deleted file mode 100644
index 2fa86c0..0000000
--- a/scripts/test-teachers-parser.js
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/usr/bin/env node
-
-/**
- * Тест парсера преподавателей
- * Запуск: node scripts/test-teachers-parser.js
- */
-
-const { JSDOM } = require('jsdom');
-const path = require('path');
-const fs = require('fs');
-
-// Импортируем парсер
-const { parseTeachersList } = require('./src/app/parser/teachers-list');
-
-// HTML с сервера (сохраните в файл или передайте через аргумент)
-const testHtml = `
-
-
-
-
-
-`;
-
-console.log('=== Testing Teachers Parser ===\n');
-
-// Создаём JSDOM
-const dom = new JSDOM(testHtml, { url: 'https://lk.ks.psuti.ru/?mn=3' });
-const document = dom.window.document;
-
-// Проверяем, находит ли селектор ссылки
-const links = Array.from(document.querySelectorAll('a[href*="?mn=3&obj="], a[href*="mn=3&obj="]'));
-console.log(`Links found by selector: ${links.length}`);
-links.forEach((link, i) => {
- console.log(` ${i + 1}. href="${link.getAttribute('href')}", text="${link.textContent?.trim()}"`);
-});
-
-// Запускаем парсер
-const teachers = parseTeachersList(document);
-console.log(`\nTeachers parsed: ${teachers.length}`);
-teachers.forEach((t, i) => {
- console.log(` ${i + 1}. [${t.parseId}] ${t.name}`);
-});
-
-dom.window.close();
-
-// Теперь тестируем на реальном HTML с сервера
-console.log('\n\n=== Testing with Real HTML from Server ===\n');
-
-const realHtmlPath = path.join(__dirname, 'teachers-test.html');
-if (fs.existsSync(realHtmlPath)) {
- const realHtml = fs.readFileSync(realHtmlPath, 'utf8');
- const realDom = new JSDOM(realHtml, { url: 'https://lk.ks.psuti.ru/?mn=3' });
- const realDocument = realDom.window.document;
-
- const realTeachers = parseTeachersList(realDocument);
- console.log(`Real teachers parsed: ${realTeachers.length}`);
- realTeachers.slice(0, 10).forEach((t, i) => {
- console.log(` ${i + 1}. [${t.parseId}] ${t.name}`);
- });
-
- if (realTeachers.length > 10) {
- console.log(` ... and ${realTeachers.length - 10} more`);
- }
-
- realDom.window.close();
-} else {
- console.log(`Test file not found: ${realHtmlPath}`);
- console.log('To test with real HTML, save the curl output to scripts/teachers-test.html');
- console.log('Example: curl -L "https://lk.ks.psuti.ru/?mn=3" > scripts/teachers-test.html');
-}
diff --git a/src/app/parser/schedule.ts b/src/app/parser/schedule.ts
index 144bdcd..56d2a6c 100644
--- a/src/app/parser/schedule.ts
+++ b/src/app/parser/schedule.ts
@@ -421,8 +421,7 @@ function parseTeacherSchedule(
currentWeekNumber = weekNumber
}
- // Ищем родительскую таблицу с парами
- // Сначала пробуем найти по cellpadding="1"
+ // Ищем родительскую таблицу с парами (cellpadding="1")
let parent: Element | null = anchor as Element
for (let i = 0; i < 10 && parent; i++) {
parent = parent.parentElement
@@ -431,24 +430,6 @@ function parseTeacherSchedule(
}
}
- // Если не нашли по cellpadding, ищем просто ближайшую таблицу
- if (!parent || parent.tagName !== 'TABLE') {
- parent = anchor.closest('table')
- }
-
- // Если все еще не нашли, ищем таблицу рядом с якорем
- if (!parent || parent.tagName !== 'TABLE') {
- // Ищем следующую таблицу после якоря
- let nextSibling: Node | null = anchor as Node
- while (nextSibling) {
- nextSibling = nextSibling.nextSibling
- if (nextSibling && nextSibling.nodeType === 1 && (nextSibling as Element).tagName === 'TABLE') {
- parent = nextSibling as Element
- break
- }
- }
- }
-
const lessons: Lesson[] = []
if (parent && parent.tagName === 'TABLE') {
@@ -467,8 +448,7 @@ function parseTeacherSchedule(
const endTime = (endTimeRaw || '').trim()
const subjCell = cells[2]
- // Проверяем наличие ячейки перед доступом к textContent
- const roomText = cells[3]?.textContent?.trim() || ''
+ const roomText = cells[3].textContent?.trim() || ''
// Извлекаем предмет, аудиторию и тип занятия по логике python‑парсера
let subject = ''
@@ -477,20 +457,19 @@ function parseTeacherSchedule(
let lessonType = ''
let location = ''
- // Проверяем наличие subjCell перед поиском элементов
- const bold = subjCell?.querySelector('b')
+ const bold = subjCell.querySelector('b')
if (bold) {
subject = bold.textContent?.trim() || ''
}
- const fontGreen = subjCell?.querySelector('font.t_green_10')
+ const fontGreen = subjCell.querySelector('font.t_green_10')
if (fontGreen) {
location = fontGreen.textContent?.trim() || ''
}
// Всё, что идёт после до , это строка с группой и типом занятия
let raw = ''
- if (bold && subjCell) {
+ if (bold) {
let node: ChildNode | null = bold.nextSibling
while (node) {
const nodeType = (node as any).nodeType
@@ -1069,24 +1048,7 @@ export function parsePage(
// Для расписания преподавателей используем отдельный, более надежный парсер,
// основанный на уже отлаженной python‑версии.
if (isTeacherSchedule) {
- try {
- const result = parseTeacherSchedule(document, url, shouldParseWeekNavigation)
- // Если парсер не нашел дней, пробуем fallback на parseGroupSchedule
- if (result.days.length === 0) {
- logDebug('parsePage: parseTeacherSchedule returned no days, trying fallback')
- return parseGroupSchedule(document, groupName, url, shouldParseWeekNavigation)
- }
- return result
- } catch (error) {
- // При ошибке парсинга преподавателя, пробуем fallback
- logDebug('parsePage: parseTeacherSchedule failed, trying fallback', { error })
- try {
- return parseGroupSchedule(document, groupName, url, shouldParseWeekNavigation)
- } catch (fallbackError) {
- // Если и fallback не сработал, выбрасываем оригинальную ошибку
- throw error
- }
- }
+ return parseTeacherSchedule(document, url, shouldParseWeekNavigation)
}
// Для расписания групп используем отдельный парсер, который опирается на структуру
diff --git a/src/app/parser/teachers-list.ts b/src/app/parser/teachers-list.ts
index b03c922..f51928e 100644
--- a/src/app/parser/teachers-list.ts
+++ b/src/app/parser/teachers-list.ts
@@ -13,32 +13,32 @@ export type TeacherListItem = {
*/
export function parseTeachersList(document: Document): TeacherListItem[] {
const teachers: TeacherListItem[] = []
-
- // Способ 1: Ищем все ссылки, которые содержат ?mn=3&obj= или mn=3&obj=
+
+ // Ищем все ссылки, которые содержат ?mn=3&obj=
const links = Array.from(document.querySelectorAll('a[href*="?mn=3&obj="], a[href*="mn=3&obj="]'))
-
+
for (const link of links) {
const href = link.getAttribute('href')
if (!href) continue
-
+
// Парсим URL вида ?mn=3&obj=XXX или /?mn=3&obj=XXX
const objMatch = href.match(/[?&]obj=(\d+)/)
if (!objMatch) continue
-
+
const parseId = Number(objMatch[1])
if (isNaN(parseId) || parseId <= 0) continue
-
+
// Извлекаем имя преподавателя из текста ссылки
const name = link.textContent?.trim()
if (!name || name.length === 0) continue
-
+
// Проверяем, что это не дубликат
if (!teachers.find(t => t.parseId === parseId)) {
teachers.push({ parseId, name })
}
}
-
- // Способ 2: Если не нашли ссылки, пытаемся найти в таблице
+
+ // Если не нашли ссылки, пытаемся найти в таблице
if (teachers.length === 0) {
const tables = Array.from(document.querySelectorAll('table'))
for (const table of tables) {
@@ -46,71 +46,28 @@ export function parseTeachersList(document: Document): TeacherListItem[] {
for (const row of rows) {
const link = row.querySelector('a[href*="obj="]')
if (!link) continue
-
+
const href = link.getAttribute('href')
if (!href || !href.includes('mn=3')) continue
-
+
const objMatch = href.match(/[?&]obj=(\d+)/)
if (!objMatch) continue
-
+
const parseId = Number(objMatch[1])
if (isNaN(parseId) || parseId <= 0) continue
-
+
const name = link.textContent?.trim() || row.textContent?.trim()
if (!name || name.length === 0) continue
-
+
if (!teachers.find(t => t.parseId === parseId)) {
teachers.push({ parseId, name })
}
}
}
}
-
- // Способ 3: Ищем все ссылки с obj= в URL (более общий поиск)
- if (teachers.length === 0) {
- const allLinks = Array.from(document.querySelectorAll('a[href*="obj="]'))
- for (const link of allLinks) {
- const href = link.getAttribute('href')
- if (!href) continue
-
- // Проверяем, что это mn=3 (преподаватели)
- if (!href.includes('mn=3')) continue
-
- const objMatch = href.match(/[?&]obj=(\d+)/)
- if (!objMatch) continue
-
- const parseId = Number(objMatch[1])
- if (isNaN(parseId) || parseId <= 0) continue
-
- const name = link.textContent?.trim()
- if (!name || name.length === 0) continue
-
- if (!teachers.find(t => t.parseId === parseId)) {
- teachers.push({ parseId, name })
- }
- }
- }
-
- // Способ 4: Ищем в формах и input элементах
- if (teachers.length === 0) {
- const forms = Array.from(document.querySelectorAll('form[action*="mn=3"]'))
- for (const form of forms) {
- const action = form.getAttribute('action') || ''
- const objMatch = action.match(/[?&]obj=(\d+)/)
- if (objMatch) {
- const parseId = Number(objMatch[1])
- if (!isNaN(parseId) && parseId > 0) {
- const name = form.textContent?.trim() || `Преподаватель ${parseId}`
- if (!teachers.find(t => t.parseId === parseId)) {
- teachers.push({ parseId, name })
- }
- }
- }
- }
- }
-
+
// Сортируем по имени
teachers.sort((a, b) => a.name.localeCompare(b.name))
-
+
return teachers
}
diff --git a/src/pages/api/admin/settings.ts b/src/pages/api/admin/settings.ts
index 70e9243..23fffe6 100644
--- a/src/pages/api/admin/settings.ts
+++ b/src/pages/api/admin/settings.ts
@@ -88,17 +88,11 @@ async function handler(
...(validatedDebug !== undefined && { debug: validatedDebug })
}
- try {
- saveSettings(settings)
- // Сбрасываем кеш и загружаем свежие настройки для подтверждения
- clearSettingsCache()
- const savedSettings = loadSettings(true)
- res.status(200).json({ success: true, settings: savedSettings })
- } catch (error) {
- console.error('Error saving settings:', error)
- const errorMessage = error instanceof Error ? error.message : 'Unknown error'
- res.status(500).json({ error: `Failed to save settings: ${errorMessage}` })
- }
+ saveSettings(settings)
+ // Сбрасываем кеш и загружаем свежие настройки для подтверждения
+ clearSettingsCache()
+ const savedSettings = loadSettings(true)
+ res.status(200).json({ success: true, settings: savedSettings })
return
}
}
diff --git a/src/pages/api/admin/teachers.ts b/src/pages/api/admin/teachers.ts
index 104adce..26544c7 100644
--- a/src/pages/api/admin/teachers.ts
+++ b/src/pages/api/admin/teachers.ts
@@ -27,71 +27,31 @@ async function handler(
// Парсинг и обновление списка преподавателей
try {
const url = `${PROXY_URL}/?mn=3`
- console.log(`[Teachers API] Fetching teachers list from: ${url}`)
- console.log(`[Teachers API] PROXY_URL: ${PROXY_URL}`)
-
+
// Добавляем таймаут 10 секунд для fetch запроса
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), 10000)
-
- const page = await fetch(url, {
- signal: controller.signal,
- redirect: 'follow',
- headers: {
- 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
- }
- })
+
+ const page = await fetch(url, { signal: controller.signal })
clearTimeout(timeoutId)
- console.log(`[Teachers API] Response status: ${page.status}`)
- console.log(`[Teachers API] Response URL: ${page.url}`)
- console.log(`[Teachers API] Response redirected: ${page.redirected}`)
-
+
const content = await page.text()
const contentType = page.headers.get('content-type')
- console.log(`[Teachers API] Content length: ${content.length}, Content-Type: ${contentType}`)
-
+
if (page.status !== 200 || !contentType || contentTypeParser.parse(contentType).type !== 'text/html') {
- console.error(`[Teachers API] Invalid response: status ${page.status}, contentType: ${contentType}`)
res.status(500).json({ error: `Failed to fetch teachers list: status ${page.status}` })
return
}
- // Проверяем, не редирект ли на страницу авторизации
- if (content.includes('login') || content.includes('auth') || content.includes('Вход') || content.includes('Авторизация')) {
- console.error('[Teachers API] Response appears to be a login page, not teachers list')
- }
-
const dom = new JSDOM(content, { url })
const document = dom.window.document
-
- // Логируем заголовок страницы для отладки
- const pageTitle = document.title
- console.log(`[Teachers API] Page title: ${pageTitle}`)
-
- // Логируем немного HTML для отладки
- const htmlPreview = content.substring(0, 500).replace(/\n/g, ' ')
- console.log(`[Teachers API] HTML preview: ${htmlPreview}...`)
-
+
const teachersList = parseTeachersList(document)
- console.log(`[Teachers API] Parsed ${teachersList.length} teachers`)
-
+
// Закрываем JSDOM для освобождения памяти
dom.window.close()
-
+
if (teachersList.length === 0) {
- console.error('[Teachers API] No teachers found in HTML')
- // Логируем больше информации для отладки
- const hasMn3 = content.includes('mn=3')
- const hasObj = content.includes('obj=')
- const hasTeachersTable = content.includes('Преподаватель') || content.includes('преподавател')
- console.log(`[Teachers API] HTML contains 'mn=3': ${hasMn3}, contains 'obj=': ${hasObj}, contains 'преподавател': ${hasTeachersTable}`)
-
- // Проверяем, не ошибка ли это
- if (content.includes('Ошибка') || content.includes('Error') || content.includes('404') || content.includes('500')) {
- console.error('[Teachers API] Response contains error indicators')
- }
-
res.status(500).json({ error: 'No teachers found on the page' })
return
}
@@ -106,23 +66,20 @@ async function handler(
name: teacher.name
}
}
- console.log(`[Teachers API] Created TeachersData with ${Object.keys(teachersData).length} entries`)
// Сохраняем в БД
saveTeachers(teachersData)
- console.log('[Teachers API] Saved teachers to database')
-
+
// Сохраняем timestamp последнего обновления
const { setTeachersLastUpdateTime } = await import('@/shared/data/database')
setTeachersLastUpdateTime(Date.now())
-
+
// Сбрасываем кеш и загружаем свежие данные из БД
clearTeachersCache()
const updatedTeachers = loadTeachers(true)
- console.log(`[Teachers API] Loaded ${Object.keys(updatedTeachers).length} teachers from database`)
-
- res.status(200).json({
- success: true,
+
+ res.status(200).json({
+ success: true,
teachers: updatedTeachers,
parsed: teachersList.length
})
diff --git a/src/shared/data/database.ts b/src/shared/data/database.ts
index ad0c974..466ed9f 100644
--- a/src/shared/data/database.ts
+++ b/src/shared/data/database.ts
@@ -9,37 +9,30 @@ import type { AppSettings } from './settings-loader'
function getDatabaseDir(): string {
// Если указан путь через переменную окружения, используем его
if (process.env.DATABASE_DIR) {
- console.log(`[Database] Using DATABASE_DIR from env: ${process.env.DATABASE_DIR}`)
return process.env.DATABASE_DIR
}
-
+
// В production режиме (standalone) используем стандартный путь
const cwd = process.cwd()
- console.log(`[Database] process.cwd(): ${cwd}`)
-
+
// Если мы в .next/standalone, поднимаемся на 2 уровня вверх к корню проекта
if (cwd.includes('.next/standalone')) {
// В standalone режиме process.cwd() = /opt/kspguti-schedule/.next/standalone
// Нужно подняться до /opt/kspguti-schedule
const standaloneMatch = cwd.match(/^(.+?)\/\.next\/standalone/)
if (standaloneMatch && standaloneMatch[1]) {
- console.log(`[Database] Detected standalone mode, using: ${standaloneMatch[1]}`)
return standaloneMatch[1]
}
// Альтернативный способ: подняться на 2 уровня вверх
- const parentDir = path.resolve(cwd, '..', '..')
- console.log(`[Database] Fallback to parent directory: ${parentDir}`)
- return parentDir
+ return path.resolve(cwd, '..', '..')
}
-
+
// Проверяем стандартный путь для production
if (fs.existsSync('/opt/kspguti-schedule')) {
- console.log('[Database] Using /opt/kspguti-schedule')
return '/opt/kspguti-schedule'
}
-
+
// В development используем текущую директорию
- console.log(`[Database] Using cwd: ${cwd}`)
return cwd
}
@@ -51,8 +44,11 @@ const DEFAULT_PASSWORD = 'ksadmin'
// Путь к старой базе данных (для миграции)
const OLD_DB_PATH = path.join(DATABASE_DIR, 'data', 'schedule-app.db')
-console.log(`[Database] DB_PATH: ${DB_PATH}`)
-console.log(`[Database] dbDir: ${path.dirname(DB_PATH)}`)
+// Создаем директорию db, если её нет
+const dbDir = path.dirname(DB_PATH)
+if (!fs.existsSync(dbDir)) {
+ fs.mkdirSync(dbDir, { recursive: true })
+}
// Миграция базы данных из data/ в db/ (если старая база существует)
function migrateDatabaseLocation(): void {
@@ -60,7 +56,7 @@ function migrateDatabaseLocation(): void {
if (fs.existsSync(DB_PATH)) {
return
}
-
+
// Если старая база существует, перемещаем её
if (fs.existsSync(OLD_DB_PATH)) {
try {
@@ -90,106 +86,31 @@ function migrateDatabaseLocation(): void {
// Инициализация базы данных
let db: Database.Database | null = null
-let dbInitAttempted = false
-let dbInitError: Error | null = null
function getDatabase(): Database.Database {
- // Если уже есть ошибка инициализации, выбрасываем её сразу
- if (dbInitError) {
- throw dbInitError
- }
-
if (db) {
return db
}
- // Защита от повторной инициализации при ошибке
- if (dbInitAttempted) {
- if (db) return db
- throw new Error('Database initialization failed previously')
- }
-
- dbInitAttempted = true
-
- console.log('[Database] Initializing database connection...')
- console.log(`[Database] DB_PATH: ${DB_PATH}`)
- console.log(`[Database] DB_PATH exists: ${fs.existsSync(DB_PATH)}`)
- console.log(`[Database] process.cwd(): ${process.cwd()}`)
- console.log(`[Database] DATABASE_DIR: ${DATABASE_DIR}`)
-
- // Создаем директорию db, если её нет
- const dbDir = path.dirname(DB_PATH)
- console.log(`[Database] dbDir: ${dbDir}`)
- console.log(`[Database] dbDir exists: ${fs.existsSync(dbDir)}`)
-
- if (!fs.existsSync(dbDir)) {
- console.log(`[Database] Creating directory: ${dbDir}`)
- try {
- fs.mkdirSync(dbDir, { recursive: true, mode: 0o755 })
- console.log(`[Database] Directory created successfully`)
- } catch (error) {
- const errMsg = `Failed to create database directory ${dbDir}: ${error}`
- console.error(`[Database] ${errMsg}`)
- dbInitError = new Error(errMsg)
- throw dbInitError
- }
- }
-
- // Проверяем, можем ли записывать в директорию
- try {
- const testFile = path.join(dbDir, '.write-test-' + Date.now())
- fs.writeFileSync(testFile, 'test', { mode: 0o644 })
- fs.unlinkSync(testFile)
- console.log('[Database] Directory is writable')
- } catch (error) {
- const errMsg = `Directory ${dbDir} is not writable: ${error}`
- console.error(`[Database] ${errMsg}`)
- dbInitError = new Error(errMsg)
- throw new Error(errMsg)
- }
-
// Выполняем миграцию расположения базы данных перед открытием
migrateDatabaseLocation()
- try {
- console.log('[Database] Opening database...')
- db = new Database(DB_PATH)
- console.log('[Database] Database opened successfully')
+ db = new Database(DB_PATH)
- // Проверяем, можем ли записывать
- try {
- db.exec('SELECT 1')
- console.log('[Database] Database is writable')
- } catch (error) {
- const errMsg = `Database is not writable: ${(error as Error).message}`
- console.error('[Database] ' + errMsg)
- dbInitError = new Error(errMsg)
- throw new Error(errMsg)
- }
+ // Применяем современные настройки SQLite
+ db.pragma('journal_mode = WAL') // Write-Ahead Logging для лучшей производительности
+ db.pragma('synchronous = NORMAL') // Баланс между производительностью и надежностью
+ db.pragma('foreign_keys = ON') // Включение проверки внешних ключей
+ db.pragma('busy_timeout = 5000') // Таймаут для ожидания блокировок (5 секунд)
+ db.pragma('temp_store = MEMORY') // Хранение временных данных в памяти
+ db.pragma('mmap_size = 268435456') // Memory-mapped I/O (256MB)
+ db.pragma('cache_size = -64000') // Размер кеша в страницах (64MB)
- // Применяем современные настройки SQLite
- db.pragma('journal_mode = WAL') // Write-Ahead Logging для лучшей производительности
- db.pragma('synchronous = NORMAL') // Баланс между производительностью и надежностью
- db.pragma('foreign_keys = ON') // Включение проверки внешних ключей
- db.pragma('busy_timeout = 5000') // Таймаут для ожидания блокировок (5 секунд)
- db.pragma('temp_store = MEMORY') // Хранение временных данных в памяти
- db.pragma('mmap_size = 268435456') // Memory-mapped I/O (256MB)
- db.pragma('cache_size = -64000') // Размер кеша в страницах (64MB)
+ // Создаем таблицы, если их нет
+ initializeTables()
- console.log('[Database] SQLite pragmas applied')
-
- // Создаем таблицы, если их нет
- initializeTables()
-
- // Выполняем миграцию данных из JSON, если БД пустая
- migrateFromJSON()
-
- console.log('[Database] Database initialization complete')
- } catch (error) {
- console.error('[Database] Failed to initialize database:', error)
- dbInitError = error as Error
- throw error
- }
+ // Выполняем миграцию данных из JSON, если БД пустая
+ migrateFromJSON()
return db
}
@@ -330,7 +251,6 @@ export function getAllTeachers(): TeachersData {
}
}
- console.log(`[Database] getAllTeachers: found ${Object.keys(teachers).length} teachers`)
return teachers
}
@@ -672,48 +592,6 @@ function migrateFromJSON(): void {
console.error('Error hashing default password:', err)
}
}
-
- // Мигрируем преподавателей из teachers.ts, если БД пустая
- const teachersCount = database.prepare('SELECT COUNT(*) as count FROM teachers').get() as { count: number }
- if (teachersCount.count === 0) {
- try {
- // Пытаемся импортировать преподавателей из teachers.ts
- const possiblePaths = [
- path.join(process.cwd(), 'src/shared/data/teachers.ts'),
- path.join(process.cwd(), '.next/standalone/src/shared/data/teachers.ts'),
- path.join(process.cwd(), 'teachers.ts')
- ]
-
- for (const filePath of possiblePaths) {
- if (fs.existsSync(filePath)) {
- console.log(`Migrating teachers from ${filePath}...`)
- // Читаем файл и извлекаем JSON массив
- const fileContents = fs.readFileSync(filePath, 'utf8')
- const jsonMatch = fileContents.match(/export const teachers = (\[[\s\S]*?\])/)
- if (jsonMatch && jsonMatch[1]) {
- const teachersArray = JSON.parse(jsonMatch[1]) as Array<{ name: string }>
-
- const insertStmt = database.prepare('INSERT INTO teachers (id, parseId, name) VALUES (?, ?, ?)')
- const transaction = database.transaction((teachers: Array<{ name: string }>) => {
- teachers.forEach((teacher, index) => {
- if (teacher.name) {
- // Используем индекс как parseId, так как в teachers.ts нет parseId
- const id = String(index + 1)
- insertStmt.run(id, index + 1, teacher.name)
- }
- })
- })
-
- transaction(teachersArray)
- console.log(`Teachers migrated from teachers.ts: ${teachersArray.length} teachers`)
- break
- }
- }
- }
- } catch (error) {
- console.error('Error migrating teachers from teachers.ts:', error)
- }
- }
}
// Экспортируем функцию для закрытия соединения (полезно для тестов)
diff --git a/src/shared/data/teachers-loader.ts b/src/shared/data/teachers-loader.ts
index 08f086a..6e4e205 100644
--- a/src/shared/data/teachers-loader.ts
+++ b/src/shared/data/teachers-loader.ts
@@ -11,7 +11,7 @@ const CACHE_TTL_MS = 1000 * 60 // 1 минута
export function loadTeachers(forceRefresh: boolean = false): TeachersData {
const now = Date.now()
const isCacheValid = cachedTeachers !== null && !forceRefresh && (now - cacheTimestamp) < CACHE_TTL_MS
-
+
if (isCacheValid && cachedTeachers !== null) {
return cachedTeachers
}
@@ -19,7 +19,6 @@ export function loadTeachers(forceRefresh: boolean = false): TeachersData {
try {
cachedTeachers = getAllTeachersFromDB()
cacheTimestamp = now
- console.log(`[TeachersLoader] Loaded ${Object.keys(cachedTeachers).length} teachers from database`)
return cachedTeachers
} catch (error) {
console.error('Error loading teachers from database:', error)
diff --git a/systemd/kspguti-schedule.service b/systemd/kspguti-schedule.service
index 796d796..f98bb2e 100644
--- a/systemd/kspguti-schedule.service
+++ b/systemd/kspguti-schedule.service
@@ -9,7 +9,6 @@ Group=www-data
WorkingDirectory=/opt/kspguti-schedule/.next/standalone
Environment=NODE_ENV=production
Environment=NEXT_TELEMETRY_DISABLED=1
-Environment=DATABASE_DIR=/opt/kspguti-schedule
Environment=PORT=3000
Environment=HOSTNAME=0.0.0.0
# Uncomment and set your environment variables: