1
0
mirror of https://github.com/ZeroCatDev/ClassworksKV.git synced 2025-07-01 20:09:23 +00:00
ClassworksKV/scripts/batchMigrate.js

192 lines
5.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
import fs from 'fs';
import path from 'path';
import { execSync } from 'child_process';
import dotenv from 'dotenv';
// 加载环境变量
dotenv.config();
const PRISMA_DIR = path.join(process.cwd(), 'prisma');
const DATABASE_DIR = path.join(PRISMA_DIR, 'database');
const MIGRATIONS_DIR = path.join(PRISMA_DIR, 'migrations');
// 数据库 URL 环境变量映射
const DB_URL_VARS = {
mysql: 'MYSQL_DATABASE_URL',
postgres: 'PG_DATABASE_URL'
};
function copyDirectory(source, destination) {
// 如果目标目录不存在,创建它
if (!fs.existsSync(destination)) {
fs.mkdirSync(destination, { recursive: true });
}
// 读取源目录中的所有内容
const items = fs.readdirSync(source);
for (const item of items) {
const sourcePath = path.join(source, item);
const destPath = path.join(destination, item);
const stats = fs.statSync(sourcePath);
if (stats.isDirectory()) {
// 如果是目录,递归复制
copyDirectory(sourcePath, destPath);
} else {
// 如果是文件,直接复制
fs.copyFileSync(sourcePath, destPath);
}
}
}
function deleteMigrationsDir() {
if (fs.existsSync(MIGRATIONS_DIR)) {
console.log('🗑️ 删除现有的 migrations 目录...');
fs.rmSync(MIGRATIONS_DIR, { recursive: true, force: true });
}
}
// 修改 schema 文件中的数据库配置
function updateSchemaConfig(schemaPath, dbType) {
console.log(`📝 更新 schema 文件配置...`);
// 读取原始内容
let content = fs.readFileSync(schemaPath, 'utf8');
const originalContent = content;
if (dbType === 'sqlite') {
// 修改 SQLite 数据库路径为 ../../data/db.db用于迁移
content = content.replace(
/url\s*=\s*"file:..\/data\/db.db"/,
'url = "file:../../data/db.db"'
);
} else {
// 获取对应的环境变量名
const urlEnvVar = DB_URL_VARS[dbType];
if (!urlEnvVar) {
throw new Error(`未找到 ${dbType} 的数据库 URL 环境变量映射`);
}
// 替换 env("DATABASE_URL") 为对应的环境变量
content = content.replace(
/env\s*\(\s*"DATABASE_URL"\s*\)/,
`env("${urlEnvVar}")`
);
}
// 写入修改后的内容
fs.writeFileSync(schemaPath, content, 'utf8');
return originalContent;
}
// 恢复 schema 文件的原始内容,对于 SQLite 恢复为 ../data/db.db
function restoreSchema(schemaPath, dbType, originalContent) {
if (originalContent) {
console.log(`📝 恢复 schema 文件的原始内容...`);
if (dbType === 'sqlite') {
// 确保恢复为 ../data/db.db
let content = originalContent;
if (content.includes('../../data/db.db')) {
content = content.replace(
/url\s*=\s*"file:..\/..\/data\/db.db"/,
'url = "file:../data/db.db"'
);
}
fs.writeFileSync(schemaPath, content, 'utf8');
} else {
fs.writeFileSync(schemaPath, originalContent, 'utf8');
}
}
}
async function processDatabaseType(dbType) {
const schemaPath = path.join(DATABASE_DIR, dbType, 'schema.prisma');
const dbMigrationsDir = path.join(DATABASE_DIR, dbType, 'migrations');
if (!fs.existsSync(schemaPath)) {
console.log(`⚠️ 跳过 ${dbType}: schema.prisma 文件不存在`);
return;
}
let originalContent;
try {
console.log(`\n🔄 处理 ${dbType} 数据库迁移...`);
// 删除旧的迁移目录
deleteMigrationsDir();
// 修改 schema 文件配置
originalContent = updateSchemaConfig(schemaPath, dbType);
// 先尝试部署现有迁移
console.log(`📦 部署现有迁移...`);
try {
execSync(`npx prisma migrate deploy --schema=${schemaPath}`, {
stdio: 'inherit'
});
} catch (error) {
console.log(`⚠️ 部署现有迁移失败,将创建新迁移`);
}
// 执行新迁移
console.log(`📦 创建新迁移...`);
execSync(`npx prisma migrate dev --name ${new Date().toISOString().split('T')[0]} --schema=${schemaPath}`, {
stdio: 'inherit'
});
// 复制迁移文件到数据库特定目录
if (fs.existsSync(MIGRATIONS_DIR)) {
console.log(`📋 复制迁移文件到 ${dbType} 目录...`);
copyDirectory(MIGRATIONS_DIR, dbMigrationsDir);
}
console.log(`${dbType} 迁移完成`);
} catch (error) {
console.error(`${dbType} 迁移失败:`, error.message);
} finally {
// 确保无论成功还是失败都恢复原始内容,对于 SQLite 恢复为 ../data/db.db
restoreSchema(schemaPath, dbType, originalContent);
}
}
async function main() {
try {
// 确保数据库目录存在
if (!fs.existsSync(DATABASE_DIR)) {
console.error('❌ database 目录不存在');
process.exit(1);
}
// 获取所有数据库类型目录
const dbTypes = fs.readdirSync(DATABASE_DIR).filter(item => {
const itemPath = path.join(DATABASE_DIR, item);
return fs.statSync(itemPath).isDirectory();
});
console.log('📊 发现的数据库类型:', dbTypes.join(', '));
console.log('🔑 数据库配置:');
for (const [dbType, envVar] of Object.entries(DB_URL_VARS)) {
console.log(` - ${dbType}: 使用环境变量 ${envVar}`);
}
console.log(' - sqlite: 迁移时使用 ../../data/db.db完成后恢复为 ../data/db.db');
// 依次处理每个数据库类型
for (const dbType of dbTypes) {
await processDatabaseType(dbType);
}
console.log('\n🎉 所有数据库迁移处理完成!');
} catch (error) {
console.error('❌ 批量迁移失败:', error);
process.exit(1);
}
}
// 执行主函数
main().catch(error => {
console.error('❌ 程序执行失败:', error);
process.exit(1);
});