import bcrypt from 'bcryptjs' import jwt from 'jsonwebtoken' import { promises as fs } from 'fs' import path from 'path' import { encryptObject, decryptObject } from './encryption.js' const JWT_SECRET = process.env.JWT_SECRET || 'harheimertc-secret-key-change-in-production' // Handle both dev and production paths const getDataPath = (filename) => { const cwd = process.cwd() // In production (.output/server), working dir is .output if (cwd.endsWith('.output')) { return path.join(cwd, '../server/data', filename) } // In development, working dir is project root return path.join(cwd, 'server/data', filename) } const USERS_FILE = getDataPath('users.json') const SESSIONS_FILE = getDataPath('sessions.json') // Get encryption key from environment function getEncryptionKey() { return process.env.ENCRYPTION_KEY || 'local_development_encryption_key_change_in_production' } // Check if data is encrypted by trying to parse as JSON first function isEncrypted(data) { try { const parsed = JSON.parse(data.trim()) if (Array.isArray(parsed)) { return false // Unencrypted array } if (typeof parsed === 'object' && parsed !== null && !parsed.encryptedData) { return false } return false } catch (e) { // JSON parsing failed - likely encrypted base64 return true } } // Read users from file (with encryption support and migration) export async function readUsers() { try { const data = await fs.readFile(USERS_FILE, 'utf-8') const encrypted = isEncrypted(data) if (encrypted) { const encryptionKey = getEncryptionKey() try { return decryptObject(data, encryptionKey) } catch (decryptError) { console.error('Fehler beim Entschlüsseln der Benutzerdaten:', decryptError) try { const plainData = JSON.parse(data) console.warn('Entschlüsselung fehlgeschlagen, versuche als unverschlüsseltes Format zu lesen') return plainData } catch (parseError) { console.error('Konnte Benutzerdaten weder entschlüsseln noch als JSON lesen') return [] } } } else { // Plain JSON - migrate to encrypted format const users = JSON.parse(data) console.log('Migriere unverschlüsselte Benutzerdaten zu verschlüsselter Speicherung...') // Write back encrypted await writeUsers(users) return users } } catch (error) { if (error.code === 'ENOENT') { return [] } console.error('Fehler beim Lesen der Benutzerdaten:', error) return [] } } // Write users to file (always encrypted) export async function writeUsers(users) { try { const encryptionKey = getEncryptionKey() const encryptedData = encryptObject(users, encryptionKey) await fs.writeFile(USERS_FILE, encryptedData, 'utf-8') return true } catch (error) { console.error('Fehler beim Schreiben der Benutzerdaten:', error) return false } } // Read sessions from file export async function readSessions() { try { const data = await fs.readFile(SESSIONS_FILE, 'utf-8') return JSON.parse(data) } catch (error) { console.error('Fehler beim Lesen der Sessions:', error) return [] } } // Write sessions to file export async function writeSessions(sessions) { try { await fs.writeFile(SESSIONS_FILE, JSON.stringify(sessions, null, 2), 'utf-8') return true } catch (error) { console.error('Fehler beim Schreiben der Sessions:', error) return false } } // Hash password export async function hashPassword(password) { const salt = await bcrypt.genSalt(10) return await bcrypt.hash(password, salt) } // Verify password export async function verifyPassword(password, hash) { return await bcrypt.compare(password, hash) } // Generate JWT token export function generateToken(user) { return jwt.sign( { id: user.id, email: user.email, role: user.role }, JWT_SECRET, { expiresIn: '7d' } ) } // Verify JWT token export function verifyToken(token) { try { return jwt.verify(token, JWT_SECRET) } catch (error) { return null } } // Get user by ID export async function getUserById(id) { const users = await readUsers() return users.find(u => u.id === id) } // Get user by email export async function getUserByEmail(email) { const users = await readUsers() return users.find(u => u.email === email) } // Get user from token export async function getUserFromToken(token) { const decoded = verifyToken(token) if (!decoded) return null const users = await readUsers() return users.find(u => u.id === decoded.id) } // Create session export async function createSession(userId, token) { const sessions = await readSessions() const session = { id: Date.now().toString(), userId, token, createdAt: new Date().toISOString(), expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString() // 7 days } sessions.push(session) await writeSessions(sessions) return session } // Delete session export async function deleteSession(token) { const sessions = await readSessions() const filtered = sessions.filter(s => s.token !== token) await writeSessions(filtered) } // Clean expired sessions export async function cleanExpiredSessions() { const sessions = await readSessions() const now = new Date() const valid = sessions.filter(s => new Date(s.expiresAt) > now) await writeSessions(valid) }