feat: добавлена навигация по неделям с возможностью отключения через админ-панель
feat: добавлена навигация по неделям с возможностью отключения через админ-панель Реализована навигация по неделям в расписании с парсингом ссылок из HTML страницы оригинального сайта. Добавлена возможность управления навигацией через админ-панель с сохранением настроек в файл. Основные изменения: - Парсинг навигации по неделям: * Добавлены типы WeekInfo и ParseResult в парсер * Реализована функция parseWeekNavigation для извлечения ссылок с параметром wk * Парсер ищет ссылки в href, onclick, формах и других атрибутах * Автоматическое определение номеров недель из текста ссылок и контекста * Вычисление соседних недель на основе найденных данных - API и функции: * Обновлена функция getSchedule для поддержки параметра wk в URL * Обновлен getServerSideProps для чтения параметра wk из query string * Кэширование расписания с учетом недели (ключ включает group + wk) - Компоненты: * Создан компонент WeekNavigation с кнопками навигации * Интегрирована навигация в компонент Schedule * Навигация работает через изменение URL параметра wk - Система настроек: * Создан settings-loader для загрузки/сохранения настроек в JSON * Добавлен API endpoint /api/admin/settings для управления настройками * Добавлен переключатель в админ-панели для включения/выключения навигации * Настройки сохраняются в src/shared/data/settings.json и переживают перезапуски - Файлы: * src/app/parser/schedule.ts - парсинг навигации по неделям * src/app/agregator/schedule.ts - поддержка параметра wk * src/pages/[group].tsx - чтение wk из query и передача настроек * src/widgets/schedule/week-navigation.tsx - компонент навигации * src/widgets/schedule/index.tsx - интеграция навигации * src/pages/admin.tsx - управление настройками * src/shared/data/settings-loader.ts - загрузка/сохранение настроек * src/pages/api/admin/settings.ts - API для настроек * src/shared/data/settings.json - файл с настройками
This commit is contained in:
106
src/shared/data/settings-loader.ts
Normal file
106
src/shared/data/settings-loader.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
export type AppSettings = {
|
||||
weekNavigationEnabled: boolean
|
||||
}
|
||||
|
||||
let cachedSettings: AppSettings | null = null
|
||||
|
||||
const defaultSettings: AppSettings = {
|
||||
weekNavigationEnabled: true
|
||||
}
|
||||
|
||||
/**
|
||||
* Загружает настройки из JSON файла
|
||||
* Использует кеш для оптимизации в production
|
||||
*/
|
||||
export function loadSettings(): AppSettings {
|
||||
if (cachedSettings) {
|
||||
return cachedSettings
|
||||
}
|
||||
|
||||
// В production Next.js может использовать другую структуру директорий
|
||||
// Пробуем несколько путей
|
||||
const possiblePaths = [
|
||||
path.join(process.cwd(), 'src/shared/data/settings.json'),
|
||||
path.join(process.cwd(), '.next/standalone/src/shared/data/settings.json'),
|
||||
path.join(process.cwd(), 'settings.json'),
|
||||
]
|
||||
|
||||
for (const filePath of possiblePaths) {
|
||||
try {
|
||||
if (fs.existsSync(filePath)) {
|
||||
const fileContents = fs.readFileSync(filePath, 'utf8')
|
||||
const settings = JSON.parse(fileContents) as AppSettings
|
||||
|
||||
// Убеждаемся, что все обязательные поля присутствуют
|
||||
const mergedSettings: AppSettings = {
|
||||
...defaultSettings,
|
||||
...settings
|
||||
}
|
||||
|
||||
cachedSettings = mergedSettings
|
||||
return mergedSettings
|
||||
}
|
||||
} catch (error) {
|
||||
// Пробуем следующий путь
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Если файл не найден, создаем его с настройками по умолчанию
|
||||
const mainPath = path.join(process.cwd(), 'src/shared/data/settings.json')
|
||||
try {
|
||||
// Создаем директорию, если её нет
|
||||
const dir = path.dirname(mainPath)
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true })
|
||||
}
|
||||
|
||||
fs.writeFileSync(mainPath, JSON.stringify(defaultSettings, null, 2), 'utf8')
|
||||
cachedSettings = defaultSettings
|
||||
return defaultSettings
|
||||
} catch (error) {
|
||||
console.error('Error creating settings.json:', error)
|
||||
// Возвращаем настройки по умолчанию
|
||||
return defaultSettings
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохраняет настройки в JSON файл
|
||||
*/
|
||||
export function saveSettings(settings: AppSettings): void {
|
||||
// Всегда сохраняем в основной путь
|
||||
const filePath = path.join(process.cwd(), 'src/shared/data/settings.json')
|
||||
|
||||
try {
|
||||
// Создаем директорию, если её нет
|
||||
const dir = path.dirname(filePath)
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true })
|
||||
}
|
||||
|
||||
// Объединяем с настройками по умолчанию для сохранения всех полей
|
||||
const mergedSettings: AppSettings = {
|
||||
...defaultSettings,
|
||||
...settings
|
||||
}
|
||||
|
||||
fs.writeFileSync(filePath, JSON.stringify(mergedSettings, null, 2), 'utf8')
|
||||
// Сбрасываем кеш
|
||||
cachedSettings = null
|
||||
} catch (error) {
|
||||
console.error('Error saving settings.json:', error)
|
||||
throw new Error('Failed to save settings')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Сбрасывает кеш настроек (полезно после обновления файла)
|
||||
*/
|
||||
export function clearSettingsCache(): void {
|
||||
cachedSettings = null
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user