fix: database location

Founded a major mistake: db overwrites during install.  Now all scripts save db in special folder and don't overwrite it.
This commit is contained in:
kilyabin
2025-12-04 23:44:21 +04:00
parent 3f74709513
commit 56a48b4552
9 changed files with 185 additions and 14 deletions

View File

@@ -63,3 +63,8 @@ docker-compose.yml
README.md README.md
*.md *.md
# Database files
db/
*.db
*.db-shm
*.db-wal

2
.gitignore vendored
View File

@@ -45,7 +45,7 @@ next-env.d.ts
error.log error.log
# database files # database files
data/ db/
*.db *.db
*.db-shm *.db-shm
*.db-wal *.db-wal

View File

@@ -109,7 +109,7 @@ kspguti-schedule/
│ └── README.md # Documentation for old files │ └── README.md # Documentation for old files
├── scripts/ # Deployment scripts ├── scripts/ # Deployment scripts
├── systemd/ # Systemd service file ├── systemd/ # Systemd service file
├── data/ # SQLite database files ├── db/ # SQLite database files
├── components.json # shadcn/ui config ├── components.json # shadcn/ui config
├── docker-compose.yml # Docker Compose config ├── docker-compose.yml # Docker Compose config
├── Dockerfile # Docker image definition ├── Dockerfile # Docker image definition
@@ -215,11 +215,12 @@ docker-compose down
- `NEXT_PUBLIC_SITE_URL` - Site URL for canonical links and sitemap (optional) - `NEXT_PUBLIC_SITE_URL` - Site URL for canonical links and sitemap (optional)
**Database:** **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 - Groups configuration
- Application settings - Application settings
- Admin password (hashed with bcrypt) - 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 - No additional database setup required
**Note:** Admin password is stored in SQLite database. Default password is `ksadmin` - change it after first login! **Note:** Admin password is stored in SQLite database. Default password is `ksadmin` - change it after first login!

27
old/data/groups.json Normal file
View File

@@ -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
}
}

18
old/data/groups.ts Normal file
View File

@@ -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 }

View File

@@ -146,6 +146,7 @@ rsync -av --exclude='node_modules' \
--exclude='.env.test' \ --exclude='.env.test' \
--exclude='.env.test.local' \ --exclude='.env.test.local' \
--exclude='*.md' \ --exclude='*.md' \
--exclude='db/' \
"$PROJECT_DIR/" "$INSTALL_DIR/" "$PROJECT_DIR/" "$INSTALL_DIR/"
# Handle .env file # Handle .env file
@@ -162,6 +163,16 @@ else
fi fi
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) # Install dependencies (with check)
echo -e "${YELLOW}Checking dependencies...${NC}" echo -e "${YELLOW}Checking dependencies...${NC}"
cd "$INSTALL_DIR" cd "$INSTALL_DIR"

View File

@@ -103,6 +103,7 @@ case "$1" in
--exclude='.env.test.local' \ --exclude='.env.test.local' \
--exclude='*.md' \ --exclude='*.md' \
--exclude='.dependencies.hash' \ --exclude='.dependencies.hash' \
--exclude='db/' \
"$PROJECT_DIR/" "$INSTALL_DIR/" "$PROJECT_DIR/" "$INSTALL_DIR/"
# Handle .env file (preserve existing if present) # Handle .env file (preserve existing if present)
@@ -113,6 +114,15 @@ case "$1" in
cp "$PROJECT_DIR/.env" "$INSTALL_DIR/.env" cp "$PROJECT_DIR/.env" "$INSTALL_DIR/.env"
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"
else
echo -e "${GREEN}Database directory exists, preserving existing database${NC}"
fi
# Change to installation directory for build # Change to installation directory for build
cd "$INSTALL_DIR" cd "$INSTALL_DIR"

View File

@@ -17,11 +17,26 @@ const readline = require('readline');
// Определяем путь к базе данных // Определяем путь к базе данных
function findDatabase() { 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 = [ const possiblePaths = [
path.join(process.cwd(), 'data', 'schedule-app.db'), path.join(projectRoot, 'db', 'schedule-app.db'),
path.join(process.cwd(), '.next', 'standalone', 'data', '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/data/schedule-app.db',
'/opt/kspguti-schedule/.next/standalone/data/schedule-app.db',
]; ];
for (const dbPath of possiblePaths) { for (const dbPath of possiblePaths) {
@@ -56,10 +71,22 @@ async function main() {
if (!dbPath) { if (!dbPath) {
console.error('❌ Ошибка: База данных не найдена!'); console.error('❌ Ошибка: База данных не найдена!');
console.log('\nИскали в следующих местах:'); 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'); let projectRoot = process.cwd();
console.log(' - /opt/kspguti-schedule/.next/standalone/data/schedule-app.db'); 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); process.exit(1);
} }

View File

@@ -5,16 +5,85 @@ import bcrypt from 'bcrypt'
import type { GroupInfo, GroupsData } from './groups-loader' import type { GroupInfo, GroupsData } from './groups-loader'
import type { AppSettings } from './settings-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' const DEFAULT_PASSWORD = 'ksadmin'
// Создаем директорию data, если её нет // Путь к старой базе данных (для миграции)
const OLD_DB_PATH = path.join(DATABASE_DIR, 'data', 'schedule-app.db')
// Создаем директорию db, если её нет
const dbDir = path.dirname(DB_PATH) const dbDir = path.dirname(DB_PATH)
if (!fs.existsSync(dbDir)) { if (!fs.existsSync(dbDir)) {
fs.mkdirSync(dbDir, { recursive: true }) 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 let db: Database.Database | null = null
@@ -23,6 +92,9 @@ function getDatabase(): Database.Database {
return db return db
} }
// Выполняем миграцию расположения базы данных перед открытием
migrateDatabaseLocation()
db = new Database(DB_PATH) db = new Database(DB_PATH)
// Применяем современные настройки SQLite // Применяем современные настройки SQLite