So mit neuen UI Ideen und so
Dieser Commit ist enthalten in:
43
backend/scripts/migrations/0001_users_email_encrypt.js
Normale Datei
43
backend/scripts/migrations/0001_users_email_encrypt.js
Normale Datei
@ -0,0 +1,43 @@
|
||||
const CryptoJS = require('crypto-js')
|
||||
const crypto = require('crypto')
|
||||
|
||||
const FIELD_ENCRYPTION_KEY = process.env.FIELD_ENCRYPTION_KEY || 'dev_field_key_change_in_production_32chars_min!'
|
||||
|
||||
function encrypt(text) {
|
||||
if (!text) return null
|
||||
try {
|
||||
return CryptoJS.AES.encrypt(text, FIELD_ENCRYPTION_KEY).toString()
|
||||
} catch (e) {
|
||||
return text
|
||||
}
|
||||
}
|
||||
|
||||
function hash(text) {
|
||||
if (!text) return null
|
||||
return crypto.createHash('sha256').update(String(text).toLowerCase()).digest('hex')
|
||||
}
|
||||
|
||||
module.exports.up = function up(db) {
|
||||
// Ensure users table has email_hash column
|
||||
try {
|
||||
db.exec('ALTER TABLE users ADD COLUMN email_hash TEXT')
|
||||
} catch {}
|
||||
// Populate encryption/hash where missing
|
||||
const users = db.prepare('SELECT id, email FROM users').all()
|
||||
const update = db.prepare('UPDATE users SET email = ?, email_hash = ? WHERE id = ?')
|
||||
const tx = db.transaction(() => {
|
||||
for (const u of users) {
|
||||
const hasEncryptedMarker = typeof u.email === 'string' && u.email.includes('U2FsdGVkX1')
|
||||
const plainEmail = u.email
|
||||
const encrypted = hasEncryptedMarker ? u.email : encrypt(plainEmail)
|
||||
const hashed = hash(plainEmail)
|
||||
update.run(encrypted, hashed, u.id)
|
||||
}
|
||||
})
|
||||
tx()
|
||||
// Add unique constraint index for email_hash if not exists
|
||||
try {
|
||||
db.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_users_email_hash_unique ON users(email_hash)')
|
||||
} catch {}
|
||||
}
|
||||
|
||||
54
backend/scripts/run-migrations.js
Normale Datei
54
backend/scripts/run-migrations.js
Normale Datei
@ -0,0 +1,54 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const Database = require('better-sqlite3')
|
||||
|
||||
const dbPath = process.env.DATABASE_PATH || path.join(process.cwd(), 'skillmate.dev.encrypted.db')
|
||||
const db = new Database(dbPath)
|
||||
|
||||
function ensureSchemaTable() {
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS schema_version (id TEXT PRIMARY KEY, applied_at TEXT NOT NULL)`)
|
||||
}
|
||||
|
||||
function getApplied() {
|
||||
try {
|
||||
const rows = db.prepare('SELECT id FROM schema_version').all()
|
||||
return new Set(rows.map(r => r.id))
|
||||
} catch {
|
||||
return new Set()
|
||||
}
|
||||
}
|
||||
|
||||
function applyMigration(file) {
|
||||
const migration = require(file)
|
||||
const id = path.basename(file)
|
||||
const tx = db.transaction(() => {
|
||||
migration.up(db)
|
||||
db.prepare('INSERT INTO schema_version (id, applied_at) VALUES (?, ?)').run(id, new Date().toISOString())
|
||||
})
|
||||
tx()
|
||||
console.log('Applied migration:', id)
|
||||
}
|
||||
|
||||
function main() {
|
||||
ensureSchemaTable()
|
||||
const applied = getApplied()
|
||||
const dir = path.join(__dirname, 'migrations')
|
||||
if (!fs.existsSync(dir)) {
|
||||
console.log('No migrations directory found, skipping.')
|
||||
process.exit(0)
|
||||
}
|
||||
const files = fs.readdirSync(dir)
|
||||
.filter(f => f.endsWith('.js'))
|
||||
.sort()
|
||||
.map(f => path.join(dir, f))
|
||||
for (const file of files) {
|
||||
const id = path.basename(file)
|
||||
if (!applied.has(id)) {
|
||||
applyMigration(file)
|
||||
}
|
||||
}
|
||||
console.log('Migrations complete.')
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren