mirror of
https://github.com/ZeroCatDev/ClassworksKV.git
synced 2025-07-02 04:39:23 +00:00
192 lines
5.6 KiB
JavaScript
192 lines
5.6 KiB
JavaScript
#!/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);
|
||
}); |