108 lines
2.8 KiB
TypeScript
108 lines
2.8 KiB
TypeScript
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import postgres from 'postgres';
|
|
|
|
function loadEnvFile(filePath: string) {
|
|
if (!fs.existsSync(filePath)) {
|
|
return;
|
|
}
|
|
|
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
|
|
for (const rawLine of content.split(/\r?\n/)) {
|
|
const line = rawLine.trim();
|
|
|
|
if (!line || line.startsWith('#')) {
|
|
continue;
|
|
}
|
|
|
|
const separatorIndex = line.indexOf('=');
|
|
if (separatorIndex === -1) {
|
|
continue;
|
|
}
|
|
|
|
const key = line.slice(0, separatorIndex).trim();
|
|
let value = line.slice(separatorIndex + 1).trim();
|
|
|
|
if (
|
|
(value.startsWith('"') && value.endsWith('"')) ||
|
|
(value.startsWith("'") && value.endsWith("'"))
|
|
) {
|
|
value = value.slice(1, -1);
|
|
}
|
|
|
|
if (!(key in process.env)) {
|
|
process.env[key] = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
function loadLocalEnv() {
|
|
loadEnvFile(path.resolve(process.cwd(), '.env.local'));
|
|
loadEnvFile(path.resolve(process.cwd(), '.env'));
|
|
}
|
|
|
|
const ALLOW_DB_RESET_FLAG = '--allow-db-reset';
|
|
|
|
function assertSafeToReset(databaseUrl: string) {
|
|
const allowDbReset =
|
|
process.env.ALLOW_DB_RESET === 'true' ||
|
|
process.argv.includes(ALLOW_DB_RESET_FLAG);
|
|
|
|
if (!allowDbReset) {
|
|
throw new Error(
|
|
`Database reset requires explicit confirmation. Run "npm run db:reset -- ${ALLOW_DB_RESET_FLAG}".`
|
|
);
|
|
}
|
|
|
|
process.env.ALLOW_DB_RESET = 'true';
|
|
|
|
if ((process.env.NODE_ENV ?? '').toLowerCase() === 'production') {
|
|
throw new Error('Database reset is blocked in NODE_ENV=production.');
|
|
}
|
|
|
|
const parsed = new URL(databaseUrl);
|
|
const host = parsed.hostname.toLowerCase();
|
|
const databaseName = parsed.pathname.replace(/^\//, '').toLowerCase();
|
|
|
|
const blockedHostFragments = ['prod', 'production', 'rds.amazonaws.com'];
|
|
const blockedDatabaseFragments = ['prod', 'production'];
|
|
|
|
if (blockedHostFragments.some((fragment) => host.includes(fragment))) {
|
|
throw new Error(`Database reset blocked for host ${host}.`);
|
|
}
|
|
|
|
if (blockedDatabaseFragments.some((fragment) => databaseName.includes(fragment))) {
|
|
throw new Error(`Database reset blocked for database ${databaseName}.`);
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
loadLocalEnv();
|
|
|
|
const databaseUrl = process.env.DATABASE_URL?.trim();
|
|
if (!databaseUrl) {
|
|
throw new Error('DATABASE_URL is required.');
|
|
}
|
|
|
|
assertSafeToReset(databaseUrl);
|
|
|
|
const parsed = new URL(databaseUrl);
|
|
console.log(
|
|
`[db:reset] target=${parsed.hostname}/${parsed.pathname.replace(/^\//, '')}`
|
|
);
|
|
|
|
const sql = postgres(databaseUrl, { prepare: false });
|
|
|
|
try {
|
|
await sql.unsafe('DROP SCHEMA IF EXISTS public CASCADE;');
|
|
await sql.unsafe('CREATE SCHEMA public;');
|
|
await sql.unsafe('DROP SCHEMA IF EXISTS drizzle CASCADE;');
|
|
console.log('[db:reset] Database schemas recreated successfully.');
|
|
} finally {
|
|
await sql.end();
|
|
}
|
|
}
|
|
|
|
void main();
|