Files
kspguti-schedule/src/pages/api/admin/teachers.ts
kilyabin ca77a74d72 fix(groups): исправить синхронизацию и транзакции БД
- Обернуть saveGroups() и saveTeachers() в транзакции SQLite для атомарности операций
      - Экспортировать getDatabase() для использования в других модулях
      - Исправить логику синхронизации в loadGroups() для режима SCHED_MODE=kspsuti
      - Удалить дублирующие вызовы clearGroupsCache() и clearTeachersCache() из API handlers
2026-03-05 23:27:19 +04:00

95 lines
3.3 KiB
TypeScript

import type { NextApiRequest, NextApiResponse } from 'next'
import { withAuth, ApiResponse } from '@/shared/utils/api-wrapper'
import { loadTeachers, saveTeachers, clearTeachersCache, TeachersData } from '@/shared/data/teachers-loader'
import { parseTeachersList } from '@/app/parser/teachers-list'
import { JSDOM } from 'jsdom'
import { PROXY_URL } from '@/shared/constants/urls'
import contentTypeParser from 'content-type'
type ResponseData = ApiResponse<{
teachers?: TeachersData
parsed?: number
}>
async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseData>
) {
if (req.method === 'GET') {
// Получение списка преподавателей (всегда свежие данные для админ-панели)
const teachers = loadTeachers(true)
res.status(200).json({ teachers })
return
}
if (req.method === 'POST') {
// Парсинг и обновление списка преподавателей
try {
const url = `${PROXY_URL}/?mn=3`
// Добавляем таймаут 10 секунд для fetch запроса
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), 10000)
const page = await fetch(url, { signal: controller.signal })
clearTimeout(timeoutId)
const content = await page.text()
const contentType = page.headers.get('content-type')
if (page.status !== 200 || !contentType || contentTypeParser.parse(contentType).type !== 'text/html') {
res.status(500).json({ error: `Failed to fetch teachers list: status ${page.status}` })
return
}
const dom = new JSDOM(content, { url })
const document = dom.window.document
const teachersList = parseTeachersList(document)
// Закрываем JSDOM для освобождения памяти
dom.window.close()
if (teachersList.length === 0) {
res.status(500).json({ error: 'No teachers found on the page' })
return
}
// Преобразуем список в формат TeachersData
// Используем parseId как id (строковое представление)
const teachersData: TeachersData = {}
for (const teacher of teachersList) {
const id = String(teacher.parseId)
teachersData[id] = {
parseId: teacher.parseId,
name: teacher.name
}
}
// Сохраняем в БД
saveTeachers(teachersData)
// Сохраняем timestamp последнего обновления
const { setTeachersLastUpdateTime } = await import('@/shared/data/database')
setTeachersLastUpdateTime(Date.now())
// Загружаем свежие данные из БД (кеш уже сброшен в saveTeachers)
const updatedTeachers = loadTeachers(true)
res.status(200).json({
success: true,
teachers: updatedTeachers,
parsed: teachersList.length
})
return
} catch (error) {
console.error('Error parsing teachers list:', error)
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
res.status(500).json({ error: `Failed to parse teachers list: ${errorMessage}` })
return
}
}
}
export default withAuth(handler, ['GET', 'POST'])