1import { restoreDbByQsName } from "./backupDbF.mts"2import { getDbBackupDir } from "./backupDbConstantsF.mts"3import { backupDb } from "./dbToolsF.mts"4import { decomposeQsFromSec } from '../../serverF/secretsF/decomposeQsFromSecF.mts'5import { interactiveSpawn } from '../../serverF/childProcF/interactiveSpawnF.mts'6import { betLog } from "../../sharedF/betterConsLogF.mts"7import { live2SpawnThrow } from '../../serverF/childProcF/live2SpawnF.mts'8import { mkDirUnlessExist } from "../../serverF/libFs/mkdirpF.mts"9import { getQsFromSecSu } from './getQsFromSecSuF.mts'10import { maskQs } from '../../serverF/dbF/maskQsF.mts'11import { cliFlag, cliActionArg } from '../../serverF/cliArgAI.mts'12import { hasUserConfirmedGit } from '../../sharedF/gitDestructivePatternsF.mts'13import { getProcArgv } from '../../serverF/isDirectlyRunF.mts'14import { tsSec } from '../../serverF/secretsF/tsSecF.mts'15import { throPtErr } from '../../sharedF/throwErrorF/sharedErrF.mts'17import { execFileThrow } from '../../serverF/childProcF/spawnF.mts'19const ddlRe = /\b(create|drop)\s+table\b|\balter\s+table\b|\b(create|drop)\s+(unique\s+)?index\b|\btruncate(\s+table)?\b/i21export const dbconsole = async ({qsName}: {qsName: string}) => {23 const {extQs} = await getQsFromSecSu({qsName: tsSec(qsName), needSu})25 const sqlQuery = cliActionArg()26 if (sqlQuery) {27 if (ddlRe.test(sqlQuery) && !hasUserConfirmedGit(getProcArgv())) {28 throPtErr('dbconsole refuses schema-mutating DDL — write a migration instead. (override: --confirmedByUser=<secret>)', {sqlQuery})29 }31 const isMetaCmd = sqlQuery.startsWith('\\')33 let finalQuery = sqlQuery34 if (!allowWrite && !isMetaCmd) {35 // Wrap in read-only transaction to prevent mutations36 finalQuery = `BEGIN READ ONLY; ${sqlQuery}; COMMIT;`37 }39 const {stdout} = await execFileThrow({40 cmdA: ['psql', extQs, '-c', finalQuery]41 })42 console.log(stdout)43 } else {44 await interactiveSpawn({cmd: `psql '${extQs}'`, printCmd: `psql '${maskQs(extQs)}'`})45 }46}47dbconsole.cliSchema = {48 'no-su': { flag: true, desc: 'use app user instead of superuser' },49 write: { flag: true, desc: 'allow mutations' },50 confirmedByUser: { type: 'string', hidden: true, desc: 'override schema-mutating DDL guard (prefer a migration)' },51}52dbconsole.cliAcceptsPositional = true53dbconsole.cliDescript = 'opens psql console, or runs query: dbconsole "SELECT..." (--write for mutations, --no-su for app user)'55export const restore = async ({qsName}: {qsName: string}) => {56 await restoreDbByQsName({qsName})57}58restore.cliDescript = `restore most recent .sql from ~/ptdisk/backups/<dbName>/`60export const backupsdir = async ({qsName}: {qsName: string}) => {61 const {svcName: dbName} = await decomposeQsFromSec(tsSec(qsName))63 const dirPath = getDbBackupDir({dbName})64 await mkDirUnlessExist(dirPath)65 await live2SpawnThrow({cmd: `open ${dirPath}`})67}69export const backup = async ({qsName}: {qsName: string}) => {71}72backup.cliDescript = `backup .sql to ~/ptdisk/backups/<dbName>/`