feat: smart note history with manual/auto modes, delete entries, i18n fixes
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 1m16s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 1m16s
- Add noteHistoryMode setting (manual default / auto) with DB migration - Manual mode: commit button in editor toolbar creates snapshots on demand - Auto mode: smart snapshots with 20-char diff threshold + 5min cooldown, structural changes (color, pin, archive, labels) bypass cooldown - Add delete individual history entries from history modal - Fix sidebar: Notes nav no longer active on notebook pages - Fix sidebar icon: replace filled Lightbulb with outlined FileText - Fix title suggestions: change from amber to sky blue color scheme - Fix hydration mismatch: add suppressHydrationWarning on locale dates - Complete i18n: add history, sort, and AI chat translations for all 16 languages - Translate French AI assistant section (40+ keys) from English to French - Update README with new features and stack info Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
76
memento-note/scripts/safe-migrate.js
Normal file
76
memento-note/scripts/safe-migrate.js
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env node
|
||||
/* eslint-disable no-console */
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const { spawnSync } = require('child_process')
|
||||
require('dotenv').config({ path: path.join(__dirname, '..', '.env') })
|
||||
|
||||
function run(command, args, options = {}) {
|
||||
const result = spawnSync(command, args, {
|
||||
stdio: 'inherit',
|
||||
shell: process.platform === 'win32',
|
||||
...options,
|
||||
})
|
||||
if (result.status !== 0) {
|
||||
process.exit(result.status || 1)
|
||||
}
|
||||
}
|
||||
|
||||
function nowStamp() {
|
||||
const d = new Date()
|
||||
const pad = (n) => String(n).padStart(2, '0')
|
||||
return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}_${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`
|
||||
}
|
||||
|
||||
const databaseUrl = process.env.DATABASE_URL
|
||||
if (!databaseUrl) {
|
||||
console.error('[safe-migrate] DATABASE_URL is missing in environment/.env')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const backupsDir = path.join(__dirname, '..', 'backups', 'migrations')
|
||||
fs.mkdirSync(backupsDir, { recursive: true })
|
||||
|
||||
const isPostgres = databaseUrl.startsWith('postgres://') || databaseUrl.startsWith('postgresql://')
|
||||
const isSqlite = databaseUrl.startsWith('file:')
|
||||
|
||||
console.log('[safe-migrate] Starting safe migration flow')
|
||||
|
||||
if (isPostgres) {
|
||||
const backupFile = path.join(backupsDir, `pre_migrate_${nowStamp()}.sql`)
|
||||
console.log(`[safe-migrate] Creating PostgreSQL backup: ${backupFile}`)
|
||||
let dump = spawnSync(
|
||||
'pg_dump',
|
||||
['--no-owner', '--no-privileges', '--format=plain', '--file', backupFile, databaseUrl],
|
||||
{ stdio: 'inherit', shell: process.platform === 'win32' }
|
||||
)
|
||||
|
||||
if (dump.status !== 0) {
|
||||
console.warn('[safe-migrate] Local pg_dump unavailable, trying Docker fallback...')
|
||||
const pgUser = process.env.POSTGRES_USER || 'memento'
|
||||
const pgDb = process.env.POSTGRES_DB || 'memento'
|
||||
const dockerCmd = `docker exec memento-postgres pg_dump -U ${pgUser} -d ${pgDb} --no-owner --no-privileges --format=plain > "${backupFile}"`
|
||||
dump = spawnSync(dockerCmd, { stdio: 'inherit', shell: true })
|
||||
}
|
||||
|
||||
if (dump.status !== 0) {
|
||||
console.error('[safe-migrate] Backup failed (local + docker). Migration aborted to protect data.')
|
||||
process.exit(dump.status || 1)
|
||||
}
|
||||
} else if (isSqlite) {
|
||||
const dbPath = databaseUrl.replace(/^file:/, '')
|
||||
const absoluteDbPath = path.isAbsolute(dbPath) ? dbPath : path.join(__dirname, '..', dbPath)
|
||||
if (fs.existsSync(absoluteDbPath)) {
|
||||
const backupFile = path.join(backupsDir, `pre_migrate_${nowStamp()}.sqlite`)
|
||||
console.log(`[safe-migrate] Creating SQLite backup: ${backupFile}`)
|
||||
fs.copyFileSync(absoluteDbPath, backupFile)
|
||||
} else {
|
||||
console.warn(`[safe-migrate] SQLite file not found at ${absoluteDbPath}, skipping backup`)
|
||||
}
|
||||
} else {
|
||||
console.warn('[safe-migrate] Unknown DATABASE_URL protocol, skipping backup step')
|
||||
}
|
||||
|
||||
console.log('[safe-migrate] Applying migrations with prisma migrate deploy')
|
||||
run('npx', ['prisma', 'migrate', 'deploy'])
|
||||
console.log('[safe-migrate] Migration completed successfully')
|
||||
Reference in New Issue
Block a user