diff --git a/.dockerignore b/.dockerignore index 0b5f68c..b95d525 100644 --- a/.dockerignore +++ b/.dockerignore @@ -63,3 +63,8 @@ docker-compose.yml README.md *.md +# Database files +db/ +*.db +*.db-shm +*.db-wal diff --git a/.gitignore b/.gitignore index 137a3b7..f65e1f8 100644 --- a/.gitignore +++ b/.gitignore @@ -45,7 +45,7 @@ next-env.d.ts error.log # database files -data/ +db/ *.db *.db-shm *.db-wal \ No newline at end of file diff --git a/README.md b/README.md index 43c1435..48adf53 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ kspguti-schedule/ │ └── README.md # Documentation for old files ├── scripts/ # Deployment scripts ├── systemd/ # Systemd service file -├── data/ # SQLite database files +├── db/ # SQLite database files ├── components.json # shadcn/ui config ├── docker-compose.yml # Docker Compose config ├── Dockerfile # Docker image definition @@ -215,11 +215,12 @@ docker-compose down - `NEXT_PUBLIC_SITE_URL` - Site URL for canonical links and sitemap (optional) **Database:** -- The application uses SQLite database (`data/schedule-app.db`) for storing: +- The application uses SQLite database (`db/schedule-app.db`) for storing: - Groups configuration - Application settings - Admin password (hashed with bcrypt) -- Database is automatically created on first run +- Database is automatically created on first run in the `db/` directory at the project root +- Database directory is excluded from deployment scripts to preserve data - No additional database setup required **Note:** Admin password is stored in SQLite database. Default password is `ksadmin` - change it after first login! diff --git a/old/data/groups.json b/old/data/groups.json new file mode 100644 index 0000000..826cc62 --- /dev/null +++ b/old/data/groups.json @@ -0,0 +1,27 @@ +{ + "ib4k": { + "parseId": 138, + "name": "ИБ-4к", + "course": 4 + }, + "ib5": { + "parseId": 144, + "name": "ИБ-5", + "course": 3 + }, + "ib6": { + "parseId": 145, + "name": "ИБ-6", + "course": 3 + }, + "ib7k": { + "parseId": 172, + "name": "ИБ-7к", + "course": 3 + }, + "ib3": { + "parseId": 123, + "name": "ИБ-3", + "course": 4 + } +} \ No newline at end of file diff --git a/old/data/groups.ts b/old/data/groups.ts new file mode 100644 index 0000000..94f9c3e --- /dev/null +++ b/old/data/groups.ts @@ -0,0 +1,18 @@ +// Загружаем группы из JSON файла только на сервере +// На клиенте будет пустой объект, группы должны передаваться через props +let groups: { [group: string]: [number, string] } = {} + +// Используем условный require только на сервере для избежания включения fs в клиентскую сборку +if (typeof window === 'undefined') { + // Серверная сторона + try { + // eslint-disable-next-line @typescript-eslint/no-require-imports + const groupsLoader = require('./groups-loader') + groups = groupsLoader.loadGroups() + } catch (error) { + console.error('Error loading groups:', error) + groups = {} + } +} + +export { groups } diff --git a/scripts/install.sh b/scripts/install.sh index ebcae3c..830472b 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -146,6 +146,7 @@ rsync -av --exclude='node_modules' \ --exclude='.env.test' \ --exclude='.env.test.local' \ --exclude='*.md' \ + --exclude='db/' \ "$PROJECT_DIR/" "$INSTALL_DIR/" # Handle .env file @@ -162,6 +163,16 @@ else fi fi +# Создаем папку db для базы данных, если её нет +if [ ! -d "$INSTALL_DIR/db" ]; then + echo -e "${YELLOW}Creating db directory for database...${NC}" + mkdir -p "$INSTALL_DIR/db" + chmod 755 "$INSTALL_DIR/db" + echo -e "${GREEN}Database directory created at $INSTALL_DIR/db${NC}" +elif [ -d "$INSTALL_DIR/db" ]; then + echo -e "${GREEN}Database directory already exists, preserving existing database${NC}" +fi + # Install dependencies (with check) echo -e "${YELLOW}Checking dependencies...${NC}" cd "$INSTALL_DIR" diff --git a/scripts/manage.sh b/scripts/manage.sh index 64c4549..96a4c23 100755 --- a/scripts/manage.sh +++ b/scripts/manage.sh @@ -103,6 +103,7 @@ case "$1" in --exclude='.env.test.local' \ --exclude='*.md' \ --exclude='.dependencies.hash' \ + --exclude='db/' \ "$PROJECT_DIR/" "$INSTALL_DIR/" # Handle .env file (preserve existing if present) @@ -113,6 +114,15 @@ case "$1" in cp "$PROJECT_DIR/.env" "$INSTALL_DIR/.env" fi + # Убеждаемся, что папка db существует и не перезаписывается + if [ ! -d "$INSTALL_DIR/db" ]; then + echo -e "${YELLOW}Creating db directory for database...${NC}" + mkdir -p "$INSTALL_DIR/db" + chmod 755 "$INSTALL_DIR/db" + else + echo -e "${GREEN}Database directory exists, preserving existing database${NC}" + fi + # Change to installation directory for build cd "$INSTALL_DIR" diff --git a/scripts/reset-admin-password.js b/scripts/reset-admin-password.js index d1ec0e4..09aaa46 100755 --- a/scripts/reset-admin-password.js +++ b/scripts/reset-admin-password.js @@ -17,11 +17,26 @@ const readline = require('readline'); // Определяем путь к базе данных function findDatabase() { + // Определяем корень проекта (для standalone режима поднимаемся на 2 уровня вверх) + let projectRoot = process.cwd(); + if (projectRoot.includes('.next/standalone')) { + const match = projectRoot.match(/^(.+?)\/\.next\/standalone/); + if (match && match[1]) { + projectRoot = match[1]; + } else { + projectRoot = path.resolve(projectRoot, '..', '..'); + } + } + const possiblePaths = [ - path.join(process.cwd(), 'data', 'schedule-app.db'), - path.join(process.cwd(), '.next', 'standalone', 'data', 'schedule-app.db'), + path.join(projectRoot, 'db', 'schedule-app.db'), + '/opt/kspguti-schedule/db/schedule-app.db', + path.join(process.cwd(), 'db', 'schedule-app.db'), + path.join(process.cwd(), '.next', 'standalone', 'db', 'schedule-app.db'), + '/opt/kspguti-schedule/.next/standalone/db/schedule-app.db', + // Старые пути для обратной совместимости + path.join(projectRoot, 'data', 'schedule-app.db'), '/opt/kspguti-schedule/data/schedule-app.db', - '/opt/kspguti-schedule/.next/standalone/data/schedule-app.db', ]; for (const dbPath of possiblePaths) { @@ -56,10 +71,22 @@ async function main() { if (!dbPath) { console.error('❌ Ошибка: База данных не найдена!'); console.log('\nИскали в следующих местах:'); - console.log(' - ' + path.join(process.cwd(), 'data', 'schedule-app.db')); - console.log(' - ' + path.join(process.cwd(), '.next', 'standalone', 'data', 'schedule-app.db')); - console.log(' - /opt/kspguti-schedule/data/schedule-app.db'); - console.log(' - /opt/kspguti-schedule/.next/standalone/data/schedule-app.db'); + + // Определяем корень проекта для отображения + let projectRoot = process.cwd(); + if (projectRoot.includes('.next/standalone')) { + const match = projectRoot.match(/^(.+?)\/\.next\/standalone/); + if (match && match[1]) { + projectRoot = match[1]; + } else { + projectRoot = path.resolve(projectRoot, '..', '..'); + } + } + + console.log(' - ' + path.join(projectRoot, 'db', 'schedule-app.db')); + console.log(' - /opt/kspguti-schedule/db/schedule-app.db'); + console.log(' - ' + path.join(process.cwd(), 'db', 'schedule-app.db')); + console.log('\n💡 Подсказка: База данных должна находиться в папке db/ в корне проекта'); process.exit(1); } diff --git a/src/shared/data/database.ts b/src/shared/data/database.ts index 6e28158..7f4e999 100644 --- a/src/shared/data/database.ts +++ b/src/shared/data/database.ts @@ -5,16 +5,85 @@ import bcrypt from 'bcrypt' import type { GroupInfo, GroupsData } from './groups-loader' import type { AppSettings } from './settings-loader' -// Путь к файлу базы данных -const DB_PATH = path.join(process.cwd(), 'data', 'schedule-app.db') +// Определяем корень проекта для хранения базы данных +function getDatabaseDir(): string { + // Если указан путь через переменную окружения, используем его + if (process.env.DATABASE_DIR) { + return process.env.DATABASE_DIR + } + + // В production режиме (standalone) используем стандартный путь + const cwd = process.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]) { + return standaloneMatch[1] + } + // Альтернативный способ: подняться на 2 уровня вверх + return path.resolve(cwd, '..', '..') + } + + // Проверяем стандартный путь для production + if (fs.existsSync('/opt/kspguti-schedule')) { + return '/opt/kspguti-schedule' + } + + // В development используем текущую директорию + return cwd +} + +// Путь к директории базы данных +const DATABASE_DIR = getDatabaseDir() +const DB_PATH = path.join(DATABASE_DIR, 'db', 'schedule-app.db') const DEFAULT_PASSWORD = 'ksadmin' -// Создаем директорию data, если её нет +// Путь к старой базе данных (для миграции) +const OLD_DB_PATH = path.join(DATABASE_DIR, 'data', 'schedule-app.db') + +// Создаем директорию db, если её нет const dbDir = path.dirname(DB_PATH) if (!fs.existsSync(dbDir)) { fs.mkdirSync(dbDir, { recursive: true }) } +// Миграция базы данных из data/ в db/ (если старая база существует) +function migrateDatabaseLocation(): void { + // Если новая база уже существует, миграция не нужна + if (fs.existsSync(DB_PATH)) { + return + } + + // Если старая база существует, перемещаем её + if (fs.existsSync(OLD_DB_PATH)) { + try { + console.log('Migrating database from data/ to db/...') + fs.renameSync(OLD_DB_PATH, DB_PATH) + + // Также перемещаем вспомогательные файлы SQLite (WAL mode) + const oldShmPath = OLD_DB_PATH + '-shm' + const oldWalPath = OLD_DB_PATH + '-wal' + const newShmPath = DB_PATH + '-shm' + const newWalPath = DB_PATH + '-wal' + + if (fs.existsSync(oldShmPath)) { + fs.renameSync(oldShmPath, newShmPath) + } + if (fs.existsSync(oldWalPath)) { + fs.renameSync(oldWalPath, newWalPath) + } + + console.log('Database successfully migrated to db/ directory') + } catch (error) { + console.error('Error migrating database:', error) + // Не падаем, просто продолжаем работу + } + } +} + // Инициализация базы данных let db: Database.Database | null = null @@ -23,6 +92,9 @@ function getDatabase(): Database.Database { return db } + // Выполняем миграцию расположения базы данных перед открытием + migrateDatabaseLocation() + db = new Database(DB_PATH) // Применяем современные настройки SQLite