feat: align with upstream 1.5.9.9
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
#!/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)
|
||||
})
|
||||
Reference in New Issue
Block a user