Files
BYW-VoiceHub/scripts/reset-database.js
T

181 lines
4.5 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
/**
* 数据库完全重置脚本
* 删除所有表(含迁移记录表),然后从头执行迁移
* 使用方式: pnpm run db:reset
*/
import { execSync } from 'child_process'
import path from 'path'
import { config } from 'dotenv'
import postgres from 'postgres'
const colors = {
reset: '\x1b[0m',
bright: '\x1b[1m',
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
cyan: '\x1b[36m'
}
const log = (msg, color = 'reset') => console.log(`${colors[color]}${msg}${colors.reset}`)
const ok = (msg) => log(`✅ ${msg}`, 'green')
const warn = (msg) => log(`⚠️ ${msg}`, 'yellow')
const err = (msg) => log(`❌ ${msg}`, 'red')
const args = process.argv.slice(2)
const forceFlag = args.includes('--force')
// 禁止在 CI 中执行(防止误触发导致生产库被清空)
if (process.env.CI && !forceFlag) {
err('拒绝在 CI 环境中执行数据库重置;如确需强制,请使用 --force 标志并确认 CI 配置正确')
process.exit(1)
}
if (!forceFlag) {
err('危险操作:此脚本将清空整库数据')
err('请添加 --force 标志确认执行:pnpm run db:reset -- --force')
process.exit(1)
}
config({ path: path.resolve(process.cwd(), '.env') })
const NON_INTERACTIVE_ENV = {
...process.env,
CI: 'true',
DRIZZLE_KIT_FORCE: 'true',
NODE_ENV: process.env.NODE_ENV || 'production'
}
function safeExec(command, options = {}) {
try {
execSync(command, { stdio: 'inherit', ...options })
return true
} catch {
return false
}
}
function createSqlClient() {
return postgres(process.env.DATABASE_URL, { max: 1 })
}
async function dropAllTables(sql) {
log('获取 public schema 下所有表...', 'cyan')
const tables = await sql`
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
AND table_type = 'BASE TABLE'
ORDER BY table_name
`
if (tables.length === 0) {
warn('没有找到任何表')
return
}
log(`发现 ${tables.length} 张表,准备逐个删除(含 __drizzle_migrations__)...`, 'cyan')
for (const row of tables) {
const tableName = row.table_name
await sql.unsafe(`DROP TABLE IF EXISTS "${tableName}" CASCADE`)
log(` - ${tableName}`, 'reset')
}
ok(`已删除 ${tables.length} 张表`)
}
async function dropAllEnums(sql) {
log('清理枚举类型...', 'cyan')
const enums = await sql`
SELECT t.typname
FROM pg_type t
JOIN pg_namespace n ON n.oid = t.typnamespace
WHERE n.nspname = 'public'
AND t.typtype = 'e'
ORDER BY t.typname
`
if (enums.length === 0) {
return
}
for (const row of enums) {
await sql.unsafe(`DROP TYPE IF EXISTS "${row.typname}" CASCADE`)
log(` - ${row.typname}`, 'reset')
}
ok(`已清理 ${enums.length} 个枚举类型`)
}
async function dropAllSequences(sql) {
log('清理序列...', 'cyan')
const sequences = await sql`
SELECT sequence_name
FROM information_schema.sequences
WHERE sequence_schema = 'public'
ORDER BY sequence_name
`
if (sequences.length === 0) {
return
}
for (const row of sequences) {
await sql.unsafe(`DROP SEQUENCE IF EXISTS "${row.sequence_name}"`)
log(` - ${row.sequence_name}`, 'reset')
}
ok(`已清理 ${sequences.length} 个序列`)
}
async function main() {
if (!process.env.DATABASE_URL) {
err('未设置 DATABASE_URL 环境变量')
process.exit(1)
}
log('⚠️ 此操作将删除数据库所有表(含迁移记录),并从头重建', 'red')
const dbUrl = (process.env.DATABASE_URL || '')
.replace(/(postgresql:\/\/[^:@]+):[^@]+@/, '$1:***@')
.replace(/(postgres:\/\/[^:@]+):[^@]+@/, '$1:***@')
log(`目标数据库: ${dbUrl}`, 'cyan')
log('🔄 开始数据库完全重置...', 'bright')
const sql = createSqlClient()
try {
// 1. 删除所有表
await dropAllTables(sql)
// 2. 删除所有枚举
await dropAllEnums(sql)
// 3. 删除所有序列
await dropAllSequences(sql)
// 4. 从头执行迁移
log('📋 从头执行数据库迁移...', 'cyan')
if (!safeExec('pnpm run db:migrate', { env: NON_INTERACTIVE_ENV })) {
err('数据库迁移失败')
process.exit(1)
}
ok('数据库完全重置并迁移成功!')
ok('数据库现已处于全新初始状态')
} catch (error) {
err(`重置失败: ${error.message || error}`)
process.exit(1)
} finally {
await sql.end()
}
}
main().catch((e) => {
err(`未预期的错误: ${e.message || e}`)
process.exit(1)
})