refactor: упростить комментарии в коде

- Удалить избыточные комментарии об обработке ошибок
- Убрать упоминания о старых проблемах (fallback, вместо throw)
- Сократить описания debug-опций
- Упростить комментарии о кэшировании
This commit is contained in:
kilyabin
2026-03-09 23:36:14 +04:00
parent 61669a840e
commit 0508333aeb
6 changed files with 41 additions and 48 deletions

View File

@@ -57,7 +57,7 @@ export async function getSchedule(groupID: number, groupName: string, wk?: numbe
dom = null dom = null
return scheduleResult return scheduleResult
} catch(e) { } catch(e) {
// Очищаем JSDOM даже в случае ошибки // Очищаем JSDOM при ошибке
if (dom) { if (dom) {
dom.window.close() dom.window.close()
} }
@@ -118,7 +118,7 @@ export async function getSchedule(groupID: number, groupName: string, wk?: numbe
networkErrorMessage: networkError.message networkErrorMessage: networkError.message
}) })
} else if (networkError.code === 'DEPTH_ZERO_SELF_SIGNED_CERT' || networkError.message?.includes('self-signed certificate') || networkError.message?.includes('certificate')) { } else if (networkError.code === 'DEPTH_ZERO_SELF_SIGNED_CERT' || networkError.message?.includes('self-signed certificate') || networkError.message?.includes('certificate')) {
// Ошибка SSL сертификата // Обработка ошибки SSL сертификата
console.error(`SSL certificate error while fetching ${PROXY_URL}:`, { console.error(`SSL certificate error while fetching ${PROXY_URL}:`, {
code: networkError.code, code: networkError.code,
message: networkError.message, message: networkError.message,
@@ -144,7 +144,7 @@ export async function getSchedule(groupID: number, groupName: string, wk?: numbe
}) })
} }
} else { } else {
// Проверяем сообщение об ошибке на наличие упоминания сертификата // Проверка на ошибку SSL сертификата по сообщению
if (errorObj.message?.includes('self-signed certificate') || errorObj.message?.includes('certificate')) { if (errorObj.message?.includes('self-signed certificate') || errorObj.message?.includes('certificate')) {
const sslError = new Error(`В колледже что-то сломалось (проблема с сертификатом безопасности). Здесь я бессилен, проблема не на моей стороне.`) const sslError = new Error(`В колледже что-то сломалось (проблема с сертификатом безопасности). Здесь я бессилен, проблема не на моей стороне.`)
logErrorToFile(sslError, { logErrorToFile(sslError, {
@@ -207,7 +207,7 @@ export async function getTeacherSchedule(teacherID: number, teacherName: string,
dom = null dom = null
return scheduleResult return scheduleResult
} catch(e) { } catch(e) {
// Очищаем JSDOM даже в случае ошибки // Очищаем JSDOM при ошибке
if (dom) { if (dom) {
dom.window.close() dom.window.close()
} }
@@ -268,7 +268,7 @@ export async function getTeacherSchedule(teacherID: number, teacherName: string,
networkErrorMessage: networkError.message networkErrorMessage: networkError.message
}) })
} else if (networkError.code === 'DEPTH_ZERO_SELF_SIGNED_CERT' || networkError.message?.includes('self-signed certificate') || networkError.message?.includes('certificate')) { } else if (networkError.code === 'DEPTH_ZERO_SELF_SIGNED_CERT' || networkError.message?.includes('self-signed certificate') || networkError.message?.includes('certificate')) {
// Ошибка SSL сертификата // Обработка ошибки SSL сертификата
console.error(`SSL certificate error while fetching ${PROXY_URL}:`, { console.error(`SSL certificate error while fetching ${PROXY_URL}:`, {
code: networkError.code, code: networkError.code,
message: networkError.message, message: networkError.message,
@@ -294,7 +294,7 @@ export async function getTeacherSchedule(teacherID: number, teacherName: string,
}) })
} }
} else { } else {
// Проверяем сообщение об ошибке на наличие упоминания сертификата // Проверка на ошибку SSL сертификата по сообщению
if (errorObj.message?.includes('self-signed certificate') || errorObj.message?.includes('certificate')) { if (errorObj.message?.includes('self-signed certificate') || errorObj.message?.includes('certificate')) {
const sslError = new Error(`В колледже что-то сломалось (проблема с сертификатом безопасности). Здесь я бессилен, проблема не на моей стороне.`) const sslError = new Error(`В колледже что-то сломалось (проблема с сертификатом безопасности). Здесь я бессилен, проблема не на моей стороне.`)
logErrorToFile(sslError, { logErrorToFile(sslError, {

View File

@@ -671,9 +671,9 @@ const parseLesson = (row: Element, isTeacherSchedule: boolean = false): Lesson |
} }
} }
// Для преподавателей может быть другая структура - проверяем наличие данных // Для преподавателей может быть другая структура проверяем наличие данных
if (!cellText && !cellHTML) { if (!cellText && !cellHTML) {
// Вместо ошибки, используем fallback // Используем fallback для получения текста
const allText = row.textContent?.trim() || '' const allText = row.textContent?.trim() || ''
if (allText && allText.length > 10) { if (allText && allText.length > 10) {
cellText = allText cellText = allText
@@ -1146,7 +1146,6 @@ export function parsePage(
throw new Error(`Table not found for ${groupName}. Found ${tables.length} tables on the page.`) throw new Error(`Table not found for ${groupName}. Found ${tables.length} tables on the page.`)
} }
// К этому моменту table определен (иначе была бы ошибка выше)
const selectedTable = table! const selectedTable = table!
logDebug('parsePage: selected table', { groupName, rows: selectedTable.querySelectorAll('tr').length }) logDebug('parsePage: selected table', { groupName, rows: selectedTable.querySelectorAll('tr').length })

View File

@@ -123,16 +123,16 @@ export default function HomePage(props: NextSerialized<PageProps>) {
} }
const cachedSchedules = new Map<string, { lastFetched: Date, results: ScheduleResult }>() const cachedSchedules = new Map<string, { lastFetched: Date, results: ScheduleResult }>()
const maxCacheDurationInMS = 1000 * 60 * 15 // 15 минут для нормального использования кэша const maxCacheDurationInMS = 1000 * 60 * 15 // 15 минут
const fallbackCacheDurationInMS = 1000 * 60 * 60 * 24 // 24 часа для fallback кэша при ошибках парсинга const fallbackCacheDurationInMS = 1000 * 60 * 60 * 24 // 24 часа
const maxCacheSize = 50 // Максимальное количество записей в кэше (только текущие недели) const maxCacheSize = 50 // Максимальное количество записей в кэше
// Очистка старых записей из кэша // Очистка старых записей из кэша
function cleanupCache() { function cleanupCache() {
const now = Date.now() const now = Date.now()
const entriesToDelete: string[] = [] const entriesToDelete: string[] = []
// Находим устаревшие записи (используем fallback TTL для сохранения кэша при ошибках) // Находим устаревшие записи
for (const [key, value] of cachedSchedules.entries()) { for (const [key, value] of cachedSchedules.entries()) {
if (now - value.lastFetched.getTime() >= fallbackCacheDurationInMS) { if (now - value.lastFetched.getTime() >= fallbackCacheDurationInMS) {
entriesToDelete.push(key) entriesToDelete.push(key)
@@ -164,9 +164,8 @@ export async function getServerSideProps(context: GetServerSidePropsContext<{ gr
: undefined : undefined
if (group && Object.hasOwn(groups, group) && group in groups) { if (group && Object.hasOwn(groups, group) && group in groups) {
// Проверяем debug опции
const debug = settings.debug || {} const debug = settings.debug || {}
// Debug: принудительно показать ошибку // Debug: принудительно показать ошибку
if (debug.forceError) { if (debug.forceError) {
const cacheAvailableFor = Array.from(cachedSchedules.entries()) const cacheAvailableFor = Array.from(cachedSchedules.entries())
@@ -190,7 +189,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext<{ gr
} }
} }
// Debug: принудительно симулировать таймаут // Debug: симулировать таймаут
if (debug.forceTimeout) { if (debug.forceTimeout) {
const cacheAvailableFor = Array.from(cachedSchedules.entries()) const cacheAvailableFor = Array.from(cachedSchedules.entries())
.filter(([, v]) => v.lastFetched.getTime() + maxCacheDurationInMS > Date.now()) .filter(([, v]) => v.lastFetched.getTime() + maxCacheDurationInMS > Date.now())
@@ -227,7 +226,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext<{ gr
const cacheKey = group // Ключ кэша - только группа (текущая неделя) const cacheKey = group // Ключ кэша - только группа (текущая неделя)
const cachedSchedule = useCache ? cachedSchedules.get(cacheKey) : undefined const cachedSchedule = useCache ? cachedSchedules.get(cacheKey) : undefined
// Debug: принудительно использовать кэш // Debug: использовать кэш
if (debug.forceCache && cachedSchedule) { if (debug.forceCache && cachedSchedule) {
scheduleResult = cachedSchedule.results scheduleResult = cachedSchedule.results
parsedAt = cachedSchedule.lastFetched parsedAt = cachedSchedule.lastFetched
@@ -251,8 +250,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext<{ gr
cleanupCache() cleanupCache()
} }
} catch(e) { } catch(e) {
// При таймауте или любой другой ошибке используем кэш, если он доступен (fallback кэш) // При ошибке используем кэш, если он доступен
// Используем кэш независимо от возраста при ошибке парсинга
if (cachedSchedule) { if (cachedSchedule) {
scheduleResult = cachedSchedule.results scheduleResult = cachedSchedule.results
parsedAt = cachedSchedule.lastFetched parsedAt = cachedSchedule.lastFetched
@@ -266,7 +264,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext<{ gr
console.warn(`Schedule fetch error for group ${group}, using fallback cache from ${cachedSchedule.lastFetched.toISOString()} (${cacheAge} minutes old)`) console.warn(`Schedule fetch error for group ${group}, using fallback cache from ${cachedSchedule.lastFetched.toISOString()} (${cacheAge} minutes old)`)
} }
} else { } else {
// Если кэша нет, возвращаем страницу с ошибкой вместо throw // Если кэша нет, возвращаем страницу с ошибкой
const isTimeout = e instanceof ScheduleTimeoutError const isTimeout = e instanceof ScheduleTimeoutError
const errorMessageObj = e instanceof Error ? e : new Error(String(e)) const errorMessageObj = e instanceof Error ? e : new Error(String(e))
const isSSLError = errorMessageObj.message?.includes('колледже что-то сломалось') || const isSSLError = errorMessageObj.message?.includes('колледже что-то сломалось') ||
@@ -278,7 +276,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext<{ gr
errorMessageObj.cause.message?.includes('self-signed certificate') errorMessageObj.cause.message?.includes('self-signed certificate')
)) ))
// Если ошибка уже содержит нужное сообщение, используем его напрямую // Формируем сообщение об ошибке
let errorMessage: string let errorMessage: string
if (isTimeout) { if (isTimeout) {
errorMessage = 'Превышено время ожидания ответа от сервера' errorMessage = 'Превышено время ожидания ответа от сервера'
@@ -314,7 +312,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext<{ gr
} }
} }
// Debug: принудительно показать пустое расписание // Debug: показать пустое расписание
if (debug.forceEmpty) { if (debug.forceEmpty) {
scheduleResult = { scheduleResult = {
days: [], days: [],
@@ -344,9 +342,9 @@ export async function getServerSideProps(context: GetServerSidePropsContext<{ gr
const cacheAvailableFor = Array.from(cachedSchedules.entries()) const cacheAvailableFor = Array.from(cachedSchedules.entries())
.filter(([, v]) => v.lastFetched.getTime() + maxCacheDurationInMS > Date.now()) .filter(([, v]) => v.lastFetched.getTime() + maxCacheDurationInMS > Date.now())
.map(([k]) => k.split('_')[0]) // Берем только группу из ключа кэша .map(([k]) => k.split('_')[0])
// Debug: информация о кэше // Информация о кэше (debug)
const cacheInfo = debug.showCacheInfo ? { const cacheInfo = debug.showCacheInfo ? {
size: cachedSchedules.size, size: cachedSchedules.size,
entries: cachedSchedules.size entries: cachedSchedules.size

View File

@@ -202,7 +202,7 @@ export default function AdminPage({ groups: initialGroups, settings: initialSett
const loadSettingsList = () => loadData('/api/admin/settings', setSettings) const loadSettingsList = () => loadData('/api/admin/settings', setSettings)
const handleUpdateSettings = async (newSettings: AppSettings) => { const handleUpdateSettings = async (newSettings: AppSettings) => {
// Сохраняем предыдущее состояние для отката при ошибке // Сохраняем предыдущее состояние для отката
const previousSettings = settings const previousSettings = settings
// Оптимистичное обновление UI // Оптимистичное обновление UI
setSettings(newSettings) setSettings(newSettings)
@@ -218,18 +218,18 @@ export default function AdminPage({ groups: initialGroups, settings: initialSett
const data = await res.json() const data = await res.json()
if (res.ok && data.success) { if (res.ok && data.success) {
// Обновляем состояние из ответа сервера (для синхронизации) // Обновляем состояние из ответа сервера
setSettings(data.settings) setSettings(data.settings)
showToast('Настройки успешно обновлены', 'success') showToast('Настройки успешно обновлены', 'success')
} else { } else {
// Откатываем изменения при ошибке // Откат изменений при ошибке
setSettings(previousSettings) setSettings(previousSettings)
const errorMessage = data.error || 'Ошибка при обновлении настроек' const errorMessage = data.error || 'Ошибка при обновлении настроек'
setError(errorMessage) setError(errorMessage)
showToast(errorMessage, 'error') showToast(errorMessage, 'error')
} }
} catch (err) { } catch (err) {
// Откатываем изменения при ошибке // Откат изменений при ошибке
setSettings(previousSettings) setSettings(previousSettings)
const errorMessage = 'Ошибка соединения с сервером' const errorMessage = 'Ошибка соединения с сервером'
setError(errorMessage) setError(errorMessage)

View File

@@ -112,16 +112,16 @@ export default function TeacherPage(props: NextSerialized<PageProps>) {
} }
const cachedTeacherSchedules = new Map<string, { lastFetched: Date, results: ScheduleResult }>() const cachedTeacherSchedules = new Map<string, { lastFetched: Date, results: ScheduleResult }>()
const maxCacheDurationInMS = 1000 * 60 * 15 // 15 минут для нормального использования кэша const maxCacheDurationInMS = 1000 * 60 * 15 // 15 минут
const fallbackCacheDurationInMS = 1000 * 60 * 60 * 24 // 24 часа для fallback кэша при ошибках парсинга const fallbackCacheDurationInMS = 1000 * 60 * 60 * 24 // 24 часа
const maxCacheSize = 50 // Максимальное количество записей в кэше (только текущие недели) const maxCacheSize = 50 // Максимальное количество записей в кэше
// Очистка старых записей из кэша // Очистка старых записей из кэша
function cleanupCache() { function cleanupCache() {
const now = Date.now() const now = Date.now()
const entriesToDelete: string[] = [] const entriesToDelete: string[] = []
// Находим устаревшие записи (используем fallback TTL для сохранения кэша при ошибках) // Находим устаревшие записи
for (const [key, value] of cachedTeacherSchedules.entries()) { for (const [key, value] of cachedTeacherSchedules.entries()) {
if (now - value.lastFetched.getTime() >= fallbackCacheDurationInMS) { if (now - value.lastFetched.getTime() >= fallbackCacheDurationInMS) {
entriesToDelete.push(key) entriesToDelete.push(key)
@@ -169,9 +169,8 @@ export async function getServerSideProps(context: GetServerSidePropsContext<{ te
} }
} }
// Проверяем debug опции
const debug = settings.debug || {} const debug = settings.debug || {}
// Debug: принудительно показать ошибку // Debug: принудительно показать ошибку
if (debug.forceError) { if (debug.forceError) {
const cacheAvailableFor = Array.from(cachedTeacherSchedules.entries()) const cacheAvailableFor = Array.from(cachedTeacherSchedules.entries())
@@ -195,7 +194,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext<{ te
} }
} }
// Debug: принудительно симулировать таймаут // Debug: симулировать таймаут
if (debug.forceTimeout) { if (debug.forceTimeout) {
const cacheAvailableFor = Array.from(cachedTeacherSchedules.entries()) const cacheAvailableFor = Array.from(cachedTeacherSchedules.entries())
.filter(([, v]) => v.lastFetched.getTime() + maxCacheDurationInMS > Date.now()) .filter(([, v]) => v.lastFetched.getTime() + maxCacheDurationInMS > Date.now())
@@ -232,7 +231,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext<{ te
const cacheKey = `teacher_${teacherParseId}` // Ключ кэша для преподавателя const cacheKey = `teacher_${teacherParseId}` // Ключ кэша для преподавателя
const cachedSchedule = useCache ? cachedTeacherSchedules.get(cacheKey) : undefined const cachedSchedule = useCache ? cachedTeacherSchedules.get(cacheKey) : undefined
// Debug: принудительно использовать кэш // Debug: использовать кэш
if (debug.forceCache && cachedSchedule) { if (debug.forceCache && cachedSchedule) {
scheduleResult = cachedSchedule.results scheduleResult = cachedSchedule.results
parsedAt = cachedSchedule.lastFetched parsedAt = cachedSchedule.lastFetched
@@ -255,8 +254,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext<{ te
cleanupCache() cleanupCache()
} }
} catch(e) { } catch(e) {
// При таймауте или любой другой ошибке используем кэш, если он доступен (fallback кэш) // При ошибке используем кэш, если он доступен
// Используем кэш независимо от возраста при ошибке парсинга
if (cachedSchedule) { if (cachedSchedule) {
scheduleResult = cachedSchedule.results scheduleResult = cachedSchedule.results
parsedAt = cachedSchedule.lastFetched parsedAt = cachedSchedule.lastFetched
@@ -270,7 +268,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext<{ te
console.warn(`Schedule fetch error for teacher ${teacherInfo.name}, using fallback cache from ${cachedSchedule.lastFetched.toISOString()} (${cacheAge} minutes old)`) console.warn(`Schedule fetch error for teacher ${teacherInfo.name}, using fallback cache from ${cachedSchedule.lastFetched.toISOString()} (${cacheAge} minutes old)`)
} }
} else { } else {
// Если кэша нет, возвращаем страницу с ошибкой вместо throw // Если кэша нет, возвращаем страницу с ошибкой
const isTimeout = e instanceof ScheduleTimeoutError const isTimeout = e instanceof ScheduleTimeoutError
const errorMessageObj = e instanceof Error ? e : new Error(String(e)) const errorMessageObj = e instanceof Error ? e : new Error(String(e))
const isSSLError = errorMessageObj.message?.includes('колледже что-то сломалось') || const isSSLError = errorMessageObj.message?.includes('колледже что-то сломалось') ||
@@ -313,7 +311,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext<{ te
} }
} }
// Debug: принудительно показать пустое расписание // Debug: показать пустое расписание
if (debug.forceEmpty) { if (debug.forceEmpty) {
scheduleResult = { scheduleResult = {
days: [], days: [],
@@ -343,9 +341,9 @@ export async function getServerSideProps(context: GetServerSidePropsContext<{ te
const cacheAvailableFor = Array.from(cachedTeacherSchedules.entries()) const cacheAvailableFor = Array.from(cachedTeacherSchedules.entries())
.filter(([, v]) => v.lastFetched.getTime() + maxCacheDurationInMS > Date.now()) .filter(([, v]) => v.lastFetched.getTime() + maxCacheDurationInMS > Date.now())
.map(([k]) => k.split('_')[1]) // Берем parseId из ключа кэша .map(([k]) => k.split('_')[1])
// Debug: информация о кэше // Информация о кэше (debug)
const cacheInfo = debug.showCacheInfo ? { const cacheInfo = debug.showCacheInfo ? {
size: cachedTeacherSchedules.size, size: cachedTeacherSchedules.size,
entries: cachedTeacherSchedules.size entries: cachedTeacherSchedules.size

View File

@@ -381,8 +381,7 @@ export function getSettings(): AppSettings {
try { try {
const settings = JSON.parse(row.value) as Partial<AppSettings> const settings = JSON.parse(row.value) as Partial<AppSettings>
// Всегда добавляем дефолтные debug настройки (они не хранятся в БД) // Добавляем debug настройки и значения по умолчанию для обратной совместимости
// И добавляем отсутствующие поля для обратной совместимости
return { return {
weekNavigationEnabled: settings.weekNavigationEnabled ?? false, weekNavigationEnabled: settings.weekNavigationEnabled ?? false,
showAddGroupButton: settings.showAddGroupButton ?? true, showAddGroupButton: settings.showAddGroupButton ?? true,
@@ -435,12 +434,11 @@ export function updateSettings(settings: AppSettings): void {
} }
} }
// Исключаем debug из настроек перед сохранением в БД // Сохраняем настройки без debug (debug не хранится в БД)
const { debug, ...settingsWithoutDebug } = settings const { debug, ...settingsWithoutDebug } = settings
const mergedSettings: AppSettings = { const mergedSettings: AppSettings = {
...defaultSettings, ...defaultSettings,
...settingsWithoutDebug ...settingsWithoutDebug
// debug намеренно не сохраняется в БД
} }
database database