feat: add persistent store, atomic 0600 writes
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'
|
||||
import { homedir } from 'node:os'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { type ConnectionConfig, connectionConfigSchema } from './types.js'
|
||||
|
||||
export const defaultStorePath = (env: NodeJS.ProcessEnv = process.env): string =>
|
||||
env.DBMOLE_STORE ?? join(homedir(), '.config', 'dbmole', 'connections.json')
|
||||
|
||||
export const parseConnections = (items: unknown[], origin: string): ConnectionConfig[] => {
|
||||
const result: ConnectionConfig[] = []
|
||||
for (const item of items) {
|
||||
const check = connectionConfigSchema.safeParse(item)
|
||||
if (check.success) {
|
||||
result.push(check.data)
|
||||
} else {
|
||||
const name = (item as { name?: unknown })?.name ?? '<unnamed>'
|
||||
console.error(`dbmole: skipping invalid connection '${String(name)}' from ${origin}`)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export const readStore = (path: string): ConnectionConfig[] => {
|
||||
mkdirSync(dirname(path), { recursive: true, mode: 0o700 })
|
||||
if (!existsSync(path)) return []
|
||||
let parsed: unknown
|
||||
try {
|
||||
parsed = JSON.parse(readFileSync(path, 'utf8'))
|
||||
} catch {
|
||||
console.error(`dbmole: store file ${path} is not valid JSON, treating as empty`)
|
||||
return []
|
||||
}
|
||||
if (!Array.isArray(parsed)) {
|
||||
console.error(`dbmole: store file ${path} must contain a JSON array, treating as empty`)
|
||||
return []
|
||||
}
|
||||
return parseConnections(parsed, `store ${path}`)
|
||||
}
|
||||
|
||||
let writeSeq = 0
|
||||
|
||||
export const writeStore = (path: string, connections: ConnectionConfig[]): void => {
|
||||
mkdirSync(dirname(path), { recursive: true, mode: 0o700 })
|
||||
writeSeq += 1
|
||||
const tmp = `${path}.${process.pid}.${writeSeq}.tmp`
|
||||
const items = connections.map((c) => JSON.stringify(c, null, 4))
|
||||
const content = items.length === 0 ? '[]\n' : `[\n${items.join(',\n')}\n]\n`
|
||||
writeFileSync(tmp, content, { mode: 0o600 })
|
||||
renameSync(tmp, path)
|
||||
}
|
||||
Reference in New Issue
Block a user