Dieser Commit ist enthalten in:
Claude Project Manager
2025-09-20 21:31:04 +02:00
Commit 6b9b6d4f20
1821 geänderte Dateien mit 348527 neuen und 0 gelöschten Zeilen

Datei anzeigen

@ -0,0 +1,71 @@
// Migrate users from legacy unencrypted DB (skillmate.dev.db)
// to encrypted dev DB (skillmate.dev.encrypted.db)
// Usage: from backend dir run: npm run migrate-users
const path = require('path')
const Database = require('better-sqlite3')
const CryptoJS = require('crypto-js')
require('dotenv').config()
function encKey() {
return process.env.FIELD_ENCRYPTION_KEY || 'dev_field_key_change_in_production_32chars_min!'
}
function encrypt(text) {
if (!text) return null
return CryptoJS.AES.encrypt(text, encKey()).toString()
}
function sha256Lower(text) {
return CryptoJS.SHA256((text || '').toLowerCase()).toString()
}
function main() {
const legacyPath = path.join(process.cwd(), 'skillmate.dev.db')
const encPath = path.join(process.cwd(), 'skillmate.dev.encrypted.db')
const legacy = new Database(legacyPath)
const enc = new Database(encPath)
try {
const legacyUsers = legacy.prepare('SELECT id, username, email, password, role, employee_id, last_login, is_active, created_at, updated_at FROM users').all()
let migrated = 0
const existsByUsername = enc.prepare('SELECT id FROM users WHERE username = ?')
const insert = enc.prepare(`
INSERT INTO users (id, username, email, email_hash, password, role, employee_id, last_login, is_active, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`)
for (const u of legacyUsers) {
const exists = existsByUsername.get(u.username)
if (exists) continue
insert.run(
u.id,
u.username,
encrypt(u.email),
sha256Lower(u.email || ''),
u.password,
u.role,
u.employee_id || null,
u.last_login || null,
u.is_active ?? 1,
u.created_at || new Date().toISOString(),
u.updated_at || new Date().toISOString()
)
migrated++
}
console.log(`✅ Migration abgeschlossen. Übertragene Benutzer: ${migrated}`)
} catch (err) {
console.error('❌ Migration fehlgeschlagen:', err)
process.exitCode = 1
} finally {
legacy.close()
enc.close()
}
}
main()

Datei anzeigen

@ -0,0 +1,88 @@
// Purge users from DB, keeping only 'admin' and a specific email
// Usage (Windows CMD/PowerShell from backend directory):
// npm run purge-users -- --email hendrik.gebhardt@polizei.nrw.de
// If --email is omitted, defaults to 'hendrik.gebhardt@polizei.nrw.de'
const path = require('path')
const fs = require('fs')
const Database = require('better-sqlite3')
const CryptoJS = require('crypto-js')
function getDbPath() {
const envPath = process.env.DATABASE_PATH
if (envPath && envPath.trim()) return envPath
const prod = process.env.NODE_ENV === 'production'
return prod
? path.join(process.cwd(), 'data', 'skillmate.encrypted.db')
: path.join(process.cwd(), 'skillmate.dev.encrypted.db')
}
function hashLower(text) {
return CryptoJS.SHA256(String(text || '').toLowerCase()).toString()
}
function parseEmailArg() {
const idx = process.argv.indexOf('--email')
if (idx !== -1 && process.argv[idx + 1]) return process.argv[idx + 1]
return 'hendrik.gebhardt@polizei.nrw.de'
}
function backupFile(filePath) {
try {
const dir = path.dirname(filePath)
const base = path.basename(filePath)
const ts = new Date().toISOString().replace(/[:.]/g, '-').replace('T', '_').slice(0,19)
const dest = path.join(dir, `${base}.backup_${ts}`)
fs.copyFileSync(filePath, dest)
console.log(`📦 Backup erstellt: ${dest}`)
} catch (e) {
console.warn('⚠️ Konnte kein Backup erstellen:', e.message)
}
}
function main() {
const dbPath = getDbPath()
const keepEmail = parseEmailArg()
const keepHash = hashLower(keepEmail)
console.log(`Datenbank: ${dbPath}`)
console.log(`Behalte Nutzer: 'admin' und ${keepEmail}`)
if (!fs.existsSync(dbPath)) {
console.error('❌ Datenbankdatei nicht gefunden.')
process.exit(1)
}
backupFile(dbPath)
const db = new Database(dbPath)
try {
const all = db.prepare('SELECT id, username, email_hash FROM users').all()
const keep = []
const del = []
for (const u of all) {
if (u.username === 'admin') { keep.push(u); continue }
if (u.email_hash && u.email_hash === keepHash) { keep.push(u); continue }
del.push(u)
}
console.log(`Gefundene Nutzer: ${all.length}`)
console.log(`Behalte: ${keep.length} | Lösche: ${del.length}`)
const tx = db.transaction(() => {
const delStmt = db.prepare('DELETE FROM users WHERE id = ?')
for (const u of del) delStmt.run(u.id)
})
tx()
console.log('✅ Bereinigung abgeschlossen.')
} catch (err) {
console.error('❌ Fehler bei der Bereinigung:', err)
process.exitCode = 1
} finally {
db.close()
}
}
main()

Datei anzeigen

@ -0,0 +1,76 @@
// Reset the admin login to username 'admin' with password 'admin123'
// Usage: from backend directory, run `npm run reset-admin` or `node scripts/reset-admin.js`
const path = require('path')
const Database = require('better-sqlite3')
const bcrypt = require('bcryptjs')
const CryptoJS = require('crypto-js')
require('dotenv').config()
function getDbPath() {
const envPath = process.env.DATABASE_PATH
if (envPath && envPath.trim()) return envPath
const prod = process.env.NODE_ENV === 'production'
return prod
? path.join(process.cwd(), 'data', 'skillmate.encrypted.db')
: path.join(process.cwd(), 'skillmate.dev.encrypted.db')
}
function getFieldKey() {
return (
process.env.FIELD_ENCRYPTION_KEY ||
'dev_field_key_change_in_production_32chars_min!'
)
}
function encrypt(text) {
if (!text) return null
return CryptoJS.AES.encrypt(text, getFieldKey()).toString()
}
function sha256Lower(text) {
return CryptoJS.SHA256(text.toLowerCase()).toString()
}
async function main() {
const dbPath = getDbPath()
console.log(`Using database: ${dbPath}`)
const db = new Database(dbPath)
try {
const admin = db.prepare('SELECT id, username FROM users WHERE username = ?').get('admin')
const newHash = await bcrypt.hash('admin123', 12)
if (admin) {
db.prepare('UPDATE users SET password = ?, is_active = 1, role = ?, updated_at = ? WHERE id = ?')
.run(newHash, 'admin', new Date().toISOString(), admin.id)
console.log('✅ Admin-Passwort zurückgesetzt: admin / admin123')
} else {
const now = new Date().toISOString()
const email = 'admin@skillmate.local'
db.prepare(`
INSERT INTO users (id, username, email, email_hash, password, role, is_active, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`).run(
'admin-' + Date.now(),
'admin',
encrypt(email),
sha256Lower(email),
newHash,
'admin',
1,
now,
now
)
console.log('✅ Admin-Benutzer erstellt: admin / admin123')
}
} catch (err) {
console.error('❌ Fehler beim Zurücksetzen der Admin-Anmeldeinformationen:', err)
process.exitCode = 1
} finally {
db.close()
}
}
main()

Datei anzeigen

@ -0,0 +1,64 @@
// Seed skills table from frontend's SKILL_HIERARCHY definition
// Usage: from backend dir: npm run seed-skills
const fs = require('fs')
const path = require('path')
const vm = require('vm')
const Database = require('better-sqlite3')
function parseFrontendHierarchy() {
const tsPath = path.join(process.cwd(), '..', 'frontend', 'src', 'data', 'skillCategories.ts')
const src = fs.readFileSync(tsPath, 'utf8')
// Remove interface declarations and LANGUAGE_LEVELS export, keep the array literal
let code = src
.replace(/export interface[\s\S]*?\n\}/g, '')
.replace(/export const LANGUAGE_LEVELS[\s\S]*?\n\n/, '')
.replace(/export const SKILL_HIERARCHY:[^=]*=/, 'module.exports =')
const sandbox = { module: {}, exports: {} }
vm.createContext(sandbox)
vm.runInContext(code, sandbox)
return sandbox.module.exports || sandbox.exports
}
function main() {
const dbPath = path.join(process.cwd(), 'skillmate.dev.encrypted.db')
const db = new Database(dbPath)
try {
const hierarchy = parseFrontendHierarchy()
if (!Array.isArray(hierarchy)) {
throw new Error('Parsed hierarchy is not an array')
}
const insert = db.prepare(`
INSERT OR IGNORE INTO skills (id, name, category, description, requires_certification, expires_after)
VALUES (?, ?, ?, ?, ?, ?)
`)
let count = 0
for (const cat of hierarchy) {
for (const sub of (cat.subcategories || [])) {
const categoryKey = `${cat.id}.${sub.id}`
for (const sk of (sub.skills || [])) {
const id = `${categoryKey}.${sk.id}`
const name = sk.name
const description = null
const requires = cat.id === 'certifications' || sub.id === 'weapons' ? 1 : 0
const expires = cat.id === 'certifications' ? 36 : null
const res = insert.run(id, name, categoryKey, description, requires, expires)
if (res.changes > 0) count++
}
}
}
console.log(`✅ Seed abgeschlossen. Neue Skills eingefügt: ${count}`)
} catch (err) {
console.error('❌ Seed fehlgeschlagen:', err)
process.exitCode = 1
} finally {
db.close()
}
}
main()